General topics | ||||||||||||||||||||||||||
Flow control | ||||||||||||||||||||||||||
Conditional execution statements | ||||||||||||||||||||||||||
Iteration statements (loops) | ||||||||||||||||||||||||||
Jump statements | ||||||||||||||||||||||||||
Functions | ||||||||||||||||||||||||||
Function declaration | ||||||||||||||||||||||||||
Lambda function expression | ||||||||||||||||||||||||||
inline specifier | ||||||||||||||||||||||||||
Dynamic exception specifications(until C++17*) | ||||||||||||||||||||||||||
noexcept specifier(C++11) | ||||||||||||||||||||||||||
Exceptions | ||||||||||||||||||||||||||
Namespaces | ||||||||||||||||||||||||||
Types | ||||||||||||||||||||||||||
Specifiers | ||||||||||||||||||||||||||
| ||||||||||||||||||||||||||
Storage duration specifiers | ||||||||||||||||||||||||||
Initialization | ||||||||||||||||||||||||||
|
Expressions | ||||||||||||||||
Alternative representations | ||||||||||||||||
Literals | ||||||||||||||||
Boolean -Integer -Floating-point | ||||||||||||||||
Character -String -nullptr(C++11) | ||||||||||||||||
User-defined(C++11) | ||||||||||||||||
Utilities | ||||||||||||||||
Attributes(C++11) | ||||||||||||||||
Types | ||||||||||||||||
typedef declaration | ||||||||||||||||
Type alias declaration(C++11) | ||||||||||||||||
Casts | ||||||||||||||||
Memory allocation | ||||||||||||||||
Classes | ||||||||||||||||
Class-specific function properties | ||||||||||||||||
| ||||||||||||||||
Special member functions | ||||||||||||||||
Templates | ||||||||||||||||
Miscellaneous | ||||||||||||||||
Initializes an object from abrace-enclosed initializer list.
Contents |
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) | ||||||||
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:
operator[]
, where list-initialization initializes the parameter of the overloaded operatorU
in this example is not the type that is being list-initialized;U
's constructor's parameter is)The effects of list-initialization of an object of type (possibly cv-qualified)T
are:
| (since C++20) |
T
is an aggregate class and the brace-enclosed initializer list, which does not contain a designated initializer list,(since C++20) has a single initializer clause of the same or derived type (possibly cv-qualified), the object is initialized from that initializer clause (bycopy-initialization for copy-list-initialization, or bydirect-initialization for direct-list-initialization).T
is a character array and the brace-enclosed initializer list has a single initializer clause that is an appropriately-typed string literal, the array isinitialized from the string literal as usual.T
is anaggregate type,aggregate initialization is performed.T
is a class type with a default constructor,value-initialization is performed.T
is a specialization ofstd::initializer_list, the object is initialized as describedbelow.T
is a class type, the constructors ofT
are considered, in two phases:T
participate inoverload resolution against the set of arguments that consists of the initializer clauses of the brace-enclosed initializer list, with the restriction that only non-narrowing conversions are allowed. If this stage produces an explicit constructor as the best match for a copy-list-initialization, compilation fails (note, in simple copy-initialization, explicit constructors are not considered at all).
| (since C++17) |
T
is not a class type), if the brace-enclosed initializer list has only one initializer clause and eitherT
is not a reference type or is a reference type whose referenced type is same as or is a base class of the type of the initializer clause,T
isdirect-initialized (in direct-list-initialization) orcopy-initialized (in copy-list-initialization), except that narrowing conversions are not allowed.T
is a reference type that is not compatible with the type of the initializer clause:
| (until C++17) |
| (since C++17) |
T
isvalue-initialized.An object of typestd::initializer_list<E> is constructed from an initializer list as if the compiler generated andmaterialized(since C++17) aprvalue of type “array ofNconst E”, whereN is the number of initializer clauses in the initializer list; this is called the initializer list’sbacking array.
Each element of the backing array iscopy-initialized with the corresponding initializer clause of the initializer list, and thestd::initializer_list<E> object is constructed to refer to that array. A constructor or conversion function selected for the copy is required to beaccessible in the context of the initializer list. If a narrowing conversion is required to initialize any of the elements, the program is ill-formed.
The backing array has the same lifetime as any othertemporary object, except that initializing anstd::initializer_list object from the backing array extends the lifetime of the array exactly likebinding a reference to a temporary.
void f(std::initializer_list<double> il); void g(float x){ f({1, x,3});} void h(){ f({1,2,3});} struct A{ mutableint i;}; void q(std::initializer_list<A>); void r(){ q({A{1}, A{2}, A{3}});} // The initialization above will be implemented in a way roughly equivalent to below,// assuming that the compiler can construct an initializer_list object with a pair of// pointers, and with the understanding that `__b` does not outlive the call to `f`. void g(float x){constdouble __a[3]={double{1},double{x},double{3}};// backing array f(std::initializer_list<double>(__a, __a+3));} void h(){staticconstexprdouble __b[3]={double{1},double{2},double{3}};// backing array f(std::initializer_list<double>(__b, __b+3));} void r(){const A __c[3]={A{1}, A{2}, A{3}};// backing array q(std::initializer_list<A>(__c, __c+3));}
Whether all backing arrays are distinct (that is, are stored innon-overlapping objects) is unspecified:
bool fun(std::initializer_list<int> il1,std::initializer_list<int> il2){return il2.begin()== il1.begin()+1;} bool overlapping= fun({1,2,3},{2,3,4});// the result is unspecified:// the back arrays can share// storage within {1, 2, 3, 4}
List-initialization limits the allowedimplicit conversions by prohibiting the following:
T
to another floating-point type whosefloating-point conversion rank is neither greater than nor equal to that ofT
, except where the conversion result is aconstant expression and one of the following conditions is satisfied:Every initializer clause issequenced before any initializer clause that follows it in the brace-enclosed initializer list. This is in contrast with the arguments of afunction call expression, which areunsequenced(until C++17)indeterminately sequenced(since C++17).
A brace-enclosed initializer 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 brace-enclosed initializer list, so given the declarationtemplate<class T>void f(T); the expressionf({1,2,3}) is ill-formed. However, the template parameter can otherwise be deduced, as is the case forstd::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 fortype deduction using the keywordauto, which deduces any brace-enclosed initializer list asstd::initializer_list in copy-list-initialization.
Also because a brace-enclosed initializer 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 brace-enclosed initializer list of a single initializer clause of the same type, but non-aggregates considerstd::initializer_list constructors first:
struct X{};// aggregate struct Q// non-aggregate{ Q()=default; Q(Qconst&)=default; Q(std::initializer_list<Q>){}}; 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)}
Some compilers (e.g., gcc 10) only consider conversion from a pointer or a pointer-to-member tobool narrowing in C++20 mode.
Feature-test macro | Value | Std | Feature |
---|---|---|---|
__cpp_initializer_lists | 200806L | (C++11) | List-initialization andstd::initializer_list |
#include <iostream>#include <map>#include <string>#include <vector> struct Foo{std::vector<int> mem={1,2,3};// list-initialization of a non-static memberstd::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 callstd::string s2{s1,2,2};// regular constructor callstd::string s3{0x61,'a'};// initializer-list ctor is preferred to (int, char) int n2={1};// copy-list-initializationdouble d=double{1.2};// list-initialization of a prvalue, then copy-initauto s4=std::string{"HelloWorld"};// same as above, no temporary// created since C++17 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'; constint(&ar)[2]={1,2};// binds an lvalue reference to a temporary arrayint&& r1={1};// binds an 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 conversionunsignedchar 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<<' ';std::cout<<'\n'; [](...){}(d, ar, r1, uc1);// has effect of [[maybe_unused]]}
Output:
world0 1 1abcd cd aa1 a2 abc3 abcd1 2 3 -1 -2 -3
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
DR | Applied to | Behavior as published | Correct behavior |
---|---|---|---|
CWG 1288 | C++11 | list-initializing a reference with a brace-enclosed initializer list of a single initializer clause always bound the reference to a temporary | bind to that initializer clause if valid |
CWG 1290 | C++11 | the lifetime of the backing array was not correctly specified | specified same as other temporary objects |
CWG 1324 | C++11 | initialization considered first for initialization from{} | aggregate initialization considered first |
CWG 1418 | C++11 | the type of the backing array lackedconst | const added |
CWG 1467 | C++11 | same-type initialization of aggregates and character arrays was prohibited; initializer-list constructors had priority over copy constructors for single-clause lists | same-type initialization allowed; single-clause lists initialize directly |
CWG 1494 | C++11 | when list-initializing a reference with an initializer clause of an incompatible type, it was unspecified whether the temporary created is direct-list-initialized or copy-list-initialized | it depends on the kind of initialization for the reference |
CWG 2137 | C++11 | initializer-list constructors lost to copy constructors when list-initializing X from{X} | non-aggregates consider initializer-lists first |
CWG 2252 | C++17 | enumerations could be list-initialized from non-scalar values | prohibited |
CWG 2267 | C++11 | the resolution ofCWG issue 1494 made clear that temporaries could be direct-list-initialized | they are copy-list-initialized when list-initializing references |
CWG 2374 | C++17 | direct-list-initialization of an enum allowed too many source types | restricted |
CWG 2627 | C++11 | a narrow bit-field of a larger integer type can be promoted to a smaller integer type, but it was still a narrowing conversion | it is not a narrowing conversion |
CWG 2713 | C++20 | references to aggregate classes could not be initialized by designated initializer lists | allowed |
CWG 2830 | C++11 | list-initialization did not ignore the top-level cv-qualification | ignores |
CWG 2864 | C++11 | floating-point conversions that overflow were not narrowing | they are narrowing |
P1957R2 | C++11 | conversion from a pointer/pointer-to-member tobool was not narrowing | considered narrowing |
P2752R3 | C++11 | backing arrays with overlapping lifetime could not overlap | they may overlap |