Consider the following code:
#include <vector>
struct A
{
explicit A(int i_) : i(i_) {}
int i;
};
int main()
{
std::vector<int> ints;
std::vector<A> As(ints.begin(), ints.end());
}
Should the above compile? My feeling is that it should not, due to the constructor being marked explicit
.
Microsoft Visual C++ agrees, giving a clear error message: cannot convert from 'int' to 'const A'; Constructor for struct 'A' is declared 'explicit'
However, using Comeau's online compiler, the code compiles successfully.
Which is correct?
Edit:
Interestingly, changing vector
to set
(after adding an operator <
to A) causes both compilers to give an error.
However, changing vector<int>
to map<int, int>
and vector<A>
to map<A, A>
causes both compilers to accept the code!
Explicit use of the this() or super() keywords allows you to call a non-default constructor. To call a non-args default constructor or an overloaded constructor from within the same class, use the this() keyword. To call a non-default superclass constructor from a subclass, use the super() keyword.
An iterator is used to point to the memory address of the STL container classes. For better understanding, you can relate them with a pointer, to some extent. Iterators act as a bridge that connects algorithms to STL containers and allows the modifications of the data present inside the container.
implicit constructor is a term commonly used to talk about two different concepts in the language, the. implicitly declared constructor which is a default or copy constructor that will be declared for all user classes if no user defined constructor is provided (default) or no copy constructor is provided (copy).
An iterator is used to move thru the elements an STL container (vector, list, set, map, ...) in a similar way to array indexes or pointers. The * operator dereferences an iterator (ie, is used to access the element an iterator points to) , and ++ (and -- for most iterators) increments to the next element.
I looked through GCC's STL implementation and it should have similar behavior. Here's why.
vector
are initialized by a generic function template which accepts any two types X
and V
and calls new( p ) X( v )
where v
is a V
(I'm paraphrasing a bit). This allows explicit conversion.set
or map
are initialized by a private member function of _tree<T,…>
which specifically expects a T const &
to be passed in. This member function isn't a template (beyond being a member of a template), so if the initial value can't be implicitly converted to T
, the call fails. (Again I'm simplifying the code.)The standard doesn't require that explicit conversion work or that implicit conversion not work when initializing a container with a range. It simply says that the range is copied into the container. Definitely ambiguous for your purpose.
Surprising such ambiguity exists, considering how they've already refined the standard in consideration of problems like the one I had a couple weeks ago.
I think it would depend on how std::vector<A> As(Iterator,Iterator)
is implemented in your particular implementation of the STL.
This is a rather tricky question, and it might be the case that VisualStudio is right and Comeau wrong (this seems really hard to believe).
The standard if read word by word, defines that vector constructor in terms of the copy constructor (see quote), and that literally means that the object obtained by dereferencing the iterator must first be converted into the type T and then the copy constructor should be called. At this point, with an explicit constructor the code should not compile.
It seems reasonable to expect an implementation, on the other hand, to directly call a constructor taking as argument the dereferenced iterator, in which case the constructor call would be explicit and thus the code should compile. This would go against the exact wording in the quote below, as the copy constructor is defined for a given type T as a constructor that taking a single possibly constant reference to an object of type T.
I cannot think of any reasonable argument not to use the Comeau approach, and my believe (this is just personal opinion) is that the wording in the standard with respect to the complexity of the vector constructor should probably be restated as requiring only N calls to the appropriate T constructor, where appropriate would have to be defined as the constructor that matches the call T( *first )
(that is, either a constructor taking an InputIterator::value_type
(by value or possibly constant reference), or the T copy constructor after an implicit conversion from InputIterator::value_type
to T.
23.2.4.1 [lib.vector.cons]/1
Complexity: The constructor template vector(InputIterator first, InputIterator last) makes only N calls to the copy constructor of T (where N is the distance between first and last) and no reallocations if iterators first and last are of forward, bidirectional, or random access categories. It makes order N calls to the copy constructor of T and order log N reallocations if they are just input iterators.
I would like to know how the VS compiler behaves when given:
struct T1;
struct T2 {
operator T1 ();
};
struct T1 {
T1( T2 const & ) { std::cout << "T1(T2)" << std::endl; }
};
T2::operator T1() {
std::cout << "T2::operator T1" << std::endl;
return T1(*this);
}
int main() {
std::vector<T2> v2;
v2.push_back( T2() );
std::vector<T1> v1( v2.begin(), v2.end() );
}
With g++ the result is that T2::operator T1
is not called, but rather the elements in v1
are constructed directly from the elements in v2
. I would assume that with VS the compiler would use T2::operator T1
to convert from each element in v2
to a T1 element and then call the copy constructor. Is that so?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With