std::initializer_list variations
c++, c++11, initialization, initializer-list, syntax
Solution
Let's abstract away from `std::vector`. And call it `T`.
T t{a, b, c};
T t = { a, b, c };
T t({a, b, c});
The first two forms are list initialization (and the only difference between them is that if `T` is a class, for the second `explicit` constructors 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:
T t(arg);
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. `T` must 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
struct T {
T(int);
T(std::initializer_list<int>);
};
T t({1});
Since a direct initialization is just a call to the overloaded constructors, we can transform this to
void ctor(int);
void ctor(std::initializer_list<int>);
void ctor(T const&);
void ctor(T &&);
We can use both trailing functions, but we would need a user defined conversion if we picked these functions. To initialize the `T ref` parameter, list initialization will be used because this is not a direct initialization with parens (so the parameter initialization is equivalent to `T ref t = { 1 }`). The first two functions are exact matches. However, the Standard says that in such a case, when one function converts to `std::initializer_list<T>` and the other does not, then the former function wins. Therefor in this scenario, the second `ctor` would 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 `T` is an array, it will initialize an array. Take this example for a class
struct T {
T(long);
T(std::initializer_list<int>);
};
T t = { 1L };
In 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
struct T {
T(long);
template<typename A = std::initializer_list<int>>
T(A);
};
T t = { 1L };
In this case we pick the first constructor, because `1L` cannot be converted to `std::initializer_list<int>`.
Problem
What are the differences between the following three initializations with `std::initializer_list`s? ``` 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.