C++03
List initialization
In C++03 you can only use list-initialization for aggregates (C++03 [dcl.init.aggr]) and scalar (C++03 [dcl.init]/13) types:
int i = { 0 };
POD pod = { 0, 1, 2 };
List assignment
You could not use "list-assignment" anywhere in C++03. The grammar shown in [expr.ass]/1 does not allow a braced list on the right of an assignment.
C++11
List initialization
In C++11 you can use list-initialization pretty much anywhere you can create a variable (see [dcl.init] in C++11 and [dcl.init.list]/1 which lists contexts where list-initialization is allowed) e.g.
struct Base { };
struct Class : Base
{
int mem{ 0 }; // init non-static data member
Class(int i)
: Base{} // init base class
, mem{i} // init member
{
int j{i}; // init local var
int k = int{0}; // init temporary
f( { 1 } ); // init function arg
int* p = new int{1}; // new init
// int k(int()); // most vexing parse, declares function
int k{ int{} }; // ok, declares variable
int i[4]{ 1,2,3,4 }; // init array
}
Class f(int i)
{
return { i }; // init return value
}
};
Class c{1}; // init global var
Most of the initializations above declare an int
or array of int
but the same syntax can be used to call a constructor for a class type (like the two lines that construct a Class
variable)
As well as being valid in almost any context where you can initialize a variable, list-initialization also interacts well with another new feature of C++11: the std::initializer_list
class template. A constructor that takes a std::initializer_list
argument can be passed an arbitrarily-long list of values, which the constructor can iterate over via begin()
and end()
member functions of the std::initializer_list
. The main benefit of this new feature is that it allows you to initialize a container with a set of elements, e.g. vector<int> v{ 0, 1, 2, 3, 4, 5 }
rather than constructing the container and then inserting values.
List-initialization can also be used for elements within a braced-init-list, allowing nested list-initialization e.g. Map m{ {a, b}, {c, d} }
rather than Map m{ Map::value_type(a, b), Map::value_type(c, d) }
The only time list-initialization doesn't do the right thing is when trying to construct a class type by calling a constructor if the class has another constructor taking a std::initializer_list
, as list-initialization will always prefer the constructor taking a std::initializer_list
e.g.
// attempts to create vector of 5 elements, [1,1,1,1,1]
// but actually creates a vector with two elements, [5,1]
std::vector<int> v{ 5, 1 };
This doesn't call the vector(size_type, const int&)
constructor, instead of calls the vector(initializer_list<int>)
constructor.
List assignment
In C++11 you can use "list-assignment"
- when assigning to a scalar type, if the braced-init-list has a single element that is convertible (without narrowing) to the variable's type (see [expr.ass]/9)
when the left operand of the assignment is a class type with a user-defined assignment operator, in which case the braced-init-list is used to initialize the argument of the operator (see [expr.ass]/9). This includes both cases like operator=(std::initializer_list<T>)
where the elements of the braced-init-list in the right operand are convertible to T
, e.g. for the std::vector<int> v
above, v = { 1, 2, 3 }
will replace the container's contents with [1,2,3] and when the braced-init-list can be implicitly-converted to the operator's argument type, via a suitable constructor e.g.
struct A {
int i;
int j;
};
struct B {
B& operator=(const A&);
};
int main() {
B b;
b = { 0, 1 };
}
On the last line of main
the braced-init-list will be implicitly-converted to a temporary A
then the assignment operator of B
will be called with that temporary as its argument.