list initialization
list initialization (since C++11)
Initializes an object from braced-init-list.
Syntax
direct-list-initialization
T object { arg1, arg2, ... }; | (1) | |
---|---|---|
T { arg1, arg2, ... }; | (2) | |
new T { arg1, arg2, ... } | (3) | |
Class { T member { arg1, arg2, ... }; }; | (4) | |
Class::Class() : member{arg1, arg2, ...} {... | (5) | |
copy-list-initialization
T object = {arg1, arg2, ...}; | (6) | |
---|---|---|
function( { arg1, arg2, ... } ) ; | (7) | |
return { arg1, arg2, ... } ; | (8) | |
object { arg1, arg2, ... } ; | (9) | |
object = { arg1, arg2, ... } ; | (10) | |
U( { arg1, arg2, ... } ) | (11) | |
Class { T member = { arg1, arg2, ... }; }; | (12) | |
List initialization is performed in the following situations:
- direct-list-initialization (both explicit and non-explicit constructors are considered)
1) initialization of a named variable with a braced-init-list
(that is, a possibly empty brace-enclosed list of expressions or nested braced-init-list
s)
2) initialization of an unnamed temporary with a braced-init-list
3) initialization of an object with dynamic storage duration with a new-expression, where the initializer is a brace-init-list
4) in a non-static data member initializer that does not use the equals sign
5) in a member initializer list of a constructor if braced-init-list
is used
- copy-list-initialization (both explicit and non-explicit constructors are considered, but only non-explicit constructors may be called)
6) initialization of a named variable with a braced-init-list
after an equals sign
7) in a function call expression, with braced-init-list
used as an argument and list-initialization initializes the function parameter
8) in a return
statement with braced-init-list used as the return
expression and list-initialization initializes the return
ed object
9) in a subscript expression with a user-defined operator[]
, where list-initialization initializes the parameter of the overloaded operator
10) in an assignment expression, where list-initialization initializes the parameter of the overloaded operator=
11) functional cast expression or other constructor invocations, where braced-init-list
is used in place of a constructor argument. Copy-list-initialization initializes the constructor's parameter (note; the type U in this example is not the type that's being list-initialized; U's constructor's parameter is)
12) in a non-static data member initializer that uses the equals sign
Explanation
T
he effects of list initialization of an object of type T
are:
If T is an aggregate type and the initializer list has a single element of the same or derived type (possibly cv-qualified), the object is initialized from that element (by copy-initialization for copy-list-initialization, or by direct-initialization for direct-list-initialization). Otherwise, if T is a character array and the initializer list has a single element that is an appropriately-typed string literal, the array is initialized from the string literal as usual. | (since C++14) |
---|
- If
T
is an aggregate type and the initializer list has a single element of the same or derived type (possibly cv-qualified), the object is initialized from that element (by copy-initialization for copy-list-initialization, or by direct-initialization for direct-list-initialization).
(since C++14)
If the braced-init-list is empty and T is a class type with a default constructor, value-initialization is performed. Otherwise, if T is an aggregate type, aggregate initialization is performed. | (until C++14) |
---|---|
If T is an aggregate type, aggregate initialization is performed. Otherwise, If the braced-init-list is empty and T is a class type with a default constructor, value-initialization is performed. | (since C++14) |
- If the braced-init-list is empty and
T
is a class type with a default constructor, value-initialization is performed.
(until C++14)
- If
T
is an aggregate type, aggregate initialization is performed.
(since C++14)
- Otherwise, if
T
is a specialization ofstd::initializer_list
, theT
object is direct-initialized or copy-initialized, depending on context, from a prvalue of the same type initialized from (until C++17) the braced-init-list.
Otherwise, if T is a enumeration type that is either scoped or unscoped with fixed underlying type, and if the braced-init-list has only one initializer, and if the conversion from the initializer to the underlying type is non-narrowing, and if the initialization is direct-list-initialization, then the enumeration is initialized with the result of converting the initializer to its underlying type. | (since C++17) |
---|
- Otherwise, if
T
is a enumeration type that is either scoped or unscoped with fixed underlying type, and if the braced-init-list has only one initializer, and if the conversion from the initializer to the underlying type is non-narrowing, and if the initialization is direct-list-initialization, then the enumeration is initialized with the result of converting the initializer to its underlying type.
(since C++17)
- Otherwise (if
T
is not a class type), if the braced-init-list has only one element andeitherT
isn't a reference type or is a reference type that is compatible with the type of the element,T
is direct-initialized (in direct-list-initialization) or copy-initialized (in copy-list-initialization), except that narrowing conversions are not allowed.
Narrowing conversions
list-initialization limits the allowed implicit conversions by prohibiting the following:
- conversion from a floating-point type to an integer type
Notes
Every initializer clause is sequenced before any initializer clause that follows it in the braced-init-list. This is in contrast with the arguments of a function call expression, which are unsequenced.
A braced-init-list is not an expression and therefore has no type, e.g. decltype{1,2}) is ill-formed. Having no type implies that template type deduction cannot deduce a type that matches a braced-init-list, so given the declaration template<class T> void f(T the expression f{1,2,3}) is ill-formed. However, the template parameter can otherwise be deduced, as is the case for std::vector<int> v(std::istream_iterator<int>(std::cin), {}), where the iterator type is deduced by the first argument but also used in the second parameter position. A special exception is made for type deduction using the keyword auto , which deduces any braced-init-list as std::initializer_list.
Also because braced-init-list has no type, special rules for overload resolution apply when it is used as an argument to an overloaded function call.
Aggregates copy/move initialize directly from single-element braced-init-lists of the same type, but non-aggregates consider initializer_list constructors first: struct X { X() = default; X(const X&) = default; }; struct Q { Q() = default; Q(Q const&) = default; Q(std::initializer_list) {} }; int main() { X x; X x2 = X { x }; // copy-constructor (not aggregate initialization) Q q; Q q2 = Q { q }; // initializer-list constructor (not copy constructor) } | (since C++14) |
---|
Example
#include <iostream>
#include <vector>
#include <map>
#include <string>
struct Foo {
std::vector<int> mem = {1,2,3}; // list-initialization of a non-static member
std::vector<int> mem2;
Foo() : mem2{-1, -2, -3} {} // list-initialization of a member in constructor
};
std::pair<std::string, std::string> f(std::pair<std::string, std::string> p)
{
return {p.second, p.first}; // list-initialization in return statement
}
int main()
{
int n0{}; // value-initialization (to zero)
int n1{1}; // direct-list-initialization
std::string s1{'a', 'b', 'c', 'd'}; // initializer-list constructor call
std::string s2{s1, 2, 2}; // regular constructor call
std::string s3{0x61, 'a'}; // initializer-list ctor is preferred to (int, char)
int n2 = {1}; // copy-list-initialization
double d = double{1.2}; // list-initialization of a temporary, then copy-init
std::map<int, std::string> m = { // nested list-initialization
{1, "a"},
{2, {'a', 'b', 'c'} },
{3, s1}
};
std::cout << f{"hello", "world"}).first // list-initialization in function call
<< '\n';
const int (&ar)[2] = {1,2}; // binds a lvalue reference to a temporary array
int&& r1 = {1}; // binds a rvalue reference to a temporary int
// int& r2 = {2}; // error: cannot bind rvalue to a non-const lvalue ref
// int bad{1.0}; // error: narrowing conversion
unsigned char uc1{10}; // okay
// unsigned char uc2{-1}; // error: narrowing conversion
Foo f;
std::cout << n0 << ' ' << n1 << ' ' << n2 << '\n'
<< s1 << ' ' << s2 << ' ' << s3 << '\n';
for(auto p: m)
std::cout << p.first << ' ' << p.second << '\n';
for(auto n: f.mem)
std::cout << n << ' ';
for(auto n: f.mem2)
std::cout << n << ' ';
}
Output:
world
0 1 1
abcd cd aa
1 a
2 abc
3 abcd
1 2 3 -1 -2 -3
Defect reports
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
DR | Applied to | Behavior as published | Correct behavior |
---|---|---|---|
CWG 1467 | C++14 | same-type initialization of aggregates and char arrays was prohibited | same-type initialization allowed |
CWG 1467 | C++14 | std::initializer_list constructors had priority over copy constructors for single-element lists | single-element lists initialize directly |
See also
- default initialization
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
http://en.cppreference.com/w/cpp/language/list_initialization