Implicit constructor conversion works with explicit vector :: vector, only sometimes

I like to initialize 2-dimensional arrays as vector<vector<int> >(x,y) . x is passed to the constructor vector<vector<int> > , and y is passed to the constructor vector<int> , x times. Although this is apparently forbidden by C ++ 03 because the constructor is explicit , it always works even on Como. I can also call vector::assign as follows. But for some reason not vector::push_back .

  vector< vector< int > > v( 20, 40 ); // OK: convert 40 to const T& v.assign( 30, 50 ); // OK v.push_back( 2 ); // error: no conversion, no matching function, etc. 

Are the first two examples really compatible for some reason? Why can I convert 40 and 50 , but not 2 ?


Epilogue: see http://gcc.gnu.org/onlinedocs/libstdc++/ext/lwg-defects.html#438 why most compilers allow this, but the standard changes a different way.

+2
c ++ constructor type-conversion
source share
4 answers

Your assumption that Como implicitly calls the constructor explicit is most likely incorrect. The behavior is really broken, but the problem is different.

I suspect that this is a mistake in implementing the standard library that comes with Comeau and not with the main Comeau compiler (although in this case the line is blurry).

If you create a quick mannequin class that has constructor properties similar to std::vector , and try the same, you will find that the compiler correctly refuses to execute the construct.

The most likely reason it takes your code is because of the well-known formal ambiguity of the two-parameter constructor std::vector . It can be interpreted as a constructor (size, initial value)

 explicit vector(size_type n, const T& value = T(), const Allocator& = Allocator()); 

or as a template constructor (begin, end) with the last one accepting two iterators

 template <class InputIterator> vector(InputIterator first, InputIterator last, const Allocator& = Allocator()); 

The standard specification of the library explicitly states that when two integral values ​​are used as arguments, the implementation must somehow make sure that the selected constructor is selected, i.e. (size, initial value) . How this is done does not matter. This can be done at the library level, it can be hardcoded in the main compiler. This can be done in any other way.

However, in response to arguments ( 20, 40 ) , the Comeau compiler seems to mistakenly select and instantiate the last constructor using InputIterator = int . I don’t know how he manages to compile a specialized version of the constructor, since integral values ​​cannot and will not work as iterators.

If you try this one

 vector< vector< int > > v( 20U, 40 ); 

you will find that the compiler now reports an error (since it can no longer use the version of the constructor's two-terator), and explicit in the first constructor does not allow you to convert 40 to std::vector .

The same thing happens with assign . This, of course, is a drawback of the Como implementation, but, again, experiments show that, most likely, the required behavior should have been performed at the library level (the main compiler seems to work fine), and somehow it was done wrong.


In my second thought, I see that the main idea in my explanation is correct, but the details are wrong. Also, I could be wrong in calling him a problem in Como. It is possible that Como is here.

The standard says in 23.1.1 / 9 that

constructor

 template <class InputIterator> X(InputIterator f, InputIterator l, const Allocator& a = Allocator()) 

should have the same effect as:

 X(static_cast<typename X::size_type>(f), static_cast<typename X::value_type>(l), a) 

if InputIterator is an integral type

I suspect that if the above is interpreted literally, the compiler is allowed to assume that explicit static_cast meant there (well ... so to speak), and the code is legal for the same reason static_cast< std::vector<int> >(10) is legal , despite the fact that the corresponding constructor is explicit . The presence of static_cast is that it allows the compiler to use the explicit constructor.

If the Comeau compiler’s behavior is correct (and I suspect that it is indeed correct as required by the standard), I wonder if this was the committee’s goal to leave such a loophole open and allow the implementation to work on the explicit constraint, possibly present in the vector element constructor .

+5
source share

Are the first two examples truly compatible for any reason?

They are not compatible with the compiler I just tried. (gcc 4.4.1)

Why can I convert 40 and 50, but not 2?

Since the first two lines are not standard, only Como can know why their inconsistency is contradictory.

It is no accident that the standard requires explicit conversions from int types to arbitrary vectors. This is to prevent code obfuscation.

+1
source share

vector< vector< int > > v( 20, 40 ); Use a constructor that you may not be familiar with. The constructor is called here vector(iterator start, iterator end);

Inside, he specializes in the int iterator, so the first parameter is considered as a counter, and the second parameter is the value for initializing the vector. Because there is a cast when assigning the second parameter to a vector value, so the constructor of the internal vector<T>(int, const T&) will be called with a value of 40. Therefore, the internal vector is constructed with 40 0.

+1
source share

Your examples do not meet the requirements. The constructor is explicit, as you said, so you are not allowed to pass int (y) instead of the vector for the constructor (and y is not passed "x times" to the vector constructor: the second parameter is created only once, to initialize the inserted objects). Your examples do not work under gcc (4.4 and 4.5).

0
source share

All Articles