Here, Is x
a std::vector<int>
or a std::vector<std::vector<int>>
?
The other answers here address your other questions, but I wanted to address this one a little more thoroughly. When we're doing class template argument deduction, we synthesize a bunch of function templates from the constructors, and then some more from deduction guides and perform overload resolution to determine the correct template parameters.
There are quite a few constructors to std::vector<T,A>
, but most of them don't mention T
which would make T
a non-deduced context and thus not a viable option in this overload. If we pre-prune the set to only use the ones that could be viable:
template <class T> vector<T> __f(size_t, T const& ); // #2
template <class T> vector<T> __f(vector<T> const& ); // #5
template <class T> vector<T> __f(vector<T>&& ); // #6, NB this is an rvalue ref
template <class T> vector<T> __f(initializer_list<T> ); // #8
And then also this deduction guide, which I'll also simplify by dropping the allocator:
template <class InputIt>
vector<typename std::iterator_traits<InputIt>::value_type> __f(InputIt, InputIt );
Those are our 5 candidates, and we're overloading as if by [dcl.init], a call via __f({v.begin(), v.end()})
. Because this is list-initialization, we start with the initializer_list
candidates and, only if there aren't any, do we proceed to the other candidates. In this case, there is an initializer_list
candidate that is viable (#8), so we select it without even considering any of the others. That candidate deduces T
as std::vector<int>::iterator
, so we then restart the process of overload resolution to select a constructor for std::vector<std::vector<int>::iterator>
list-initialized with two iterators.
This is probably not the desired outcome - we probably wanted a vector<int>
. The solution there is simple: use ()
s:
std::vector x(v.begin(), v.end()); // uses explicit deduction guide
Now, we're not doing list-initialization so the initializer_list
candidate isn't a viable candidate. As a result, we deduce vector<int>
through the deduction guide (the only viable candidate), and end up calling the iterator-pair constructor of it. This has the side benefit of actually making the comment correct.
This is one of the many places where initializing with {}
does something wildly different than initializing with ()
. Some argue that {}
is uniform initialization - which examples like this seem to counter. My rule of thumb: use {}
when you specifically, consciously need the behavior that {}
provides. ()
otherwise.