What are the differences between the following three initializations with std::initializer_lists?
std::vector<int> a{ 2, 3, 5, 7};
std::vector<int> b( { 2, 3, 5, 7} );
std::vector<int> c = { 2, 3, 5, 7};
In the above example, std::vector is just a placeholder, but I am interested in a general answer.
Let’s abstract away from
std::vector. And call itT.The first two forms are list initialization (and the only difference between them is that if
Tis a class, for the secondexplicitconstructors are forbidden to be called. If one is called, the program becomes ill-formed). The last form is just ordinary direct initialization as we know it from C++03:That there appears a
{a, b, c}as arg means that the argument for the constructor call is a brace initializer list. This third form does not have the special handling that list initialization has.Tmust be a class type there, even if the braced init list has only 1 argument. I’m glad that we put clear rules before releasing C++11 in this case.As in terms of what constructors are called for the third, let’s assume
Since a direct initialization is just a call to the overloaded constructors, we can transform this to
We can use both trailing functions, but we would need a user defined conversion if we picked these functions. To initialize the
T refparameter, list initialization will be used because this is not a direct initialization with parens (so the parameter initialization is equivalent toT ref t = { 1 }). The first two functions are exact matches. However, the Standard says that in such a case, when one function converts tostd::initializer_list<T>and the other does not, then the former function wins. Therefor in this scenario, the secondctorwould be used. Note that in this scenario, we will not do two-phase overload resolution with first only initializer list ctors – only list initialization will do that.For the first two, we will use list-initialization, and it will do context dependent things. If
Tis an array, it will initialize an array. Take this example for a classIn this case, we do two-phase overload resolution. We first only consider initializer list constructors and see if one matches, as argument we take the whole braced init list. The second ctor matches, so we pick it. We will ignore the first constructor. If we have no initializer list ctor or if none matches, we take all ctors and the elements of the initializer list
In this case we pick the first constructor, because
1Lcannot be converted tostd::initializer_list<int>.