This question already covers what PODs and aggregates are, and provides some examples on aggregate initialization.
The question here is where can you use li
Aggregate initialization is the subset of list initialization that is limited just to aggregates and PODs (as discussed in the question you referenced). Both types of initialization make use of curly braces and optionally and an equals, so the syntax does appear the same at the point of initialization. See http://en.cppreference.com/w/cpp/language/aggregate_initialization and http://en.cppreference.com/w/cpp/language/list_initialization for more details including places where each form of initialization can be used.
In C++03 aggregate initialization could only be used with equals (i.e. T object {arg1, arg2}; wasn't valid just T object = {arg1, arg2};), while C++11 allows it without equals (i.e. T object {arg1, arg2}; became valid). Also in C++11 aggregate initialization was modified slightly to disallow narrowing conversions in aggregate initialization.
The subset of list initialization, which is not the aggregate initialization subset, was introduced in C++11.
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 };
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.
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.
In C++11 you can use "list-assignment"
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.
List-initialization can be used to initialize dynamically-allocated arrays (C++11):
int * a = new int[3] {4, 3, 2};
A very nifty feature not possible in C++03.