[dcl.init.list] # 9 Declarations [[dcl]](./#dcl) ## 9.5 Initializers [[dcl.init]](dcl.init#list) ### 9.5.5 List-initialization [dcl.init.list] [1](#1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/declarations.tex#L6366) [*List-initialization*](#def:list-initialization "9.5.5 List-initialization [dcl.init.list]") is initialization of an object or reference from a[*braced-init-list*](dcl.init.general#nt:braced-init-list "9.5.1 General [dcl.init.general]")[.](#1.sentence-1) Such an initializer is called an [*initializer list*](#def:initializer_list), and the comma-separated[*initializer-clause*](dcl.init.general#nt:initializer-clause "9.5.1 General [dcl.init.general]")*s* of the [*initializer-list*](dcl.init.general#nt:initializer-list "9.5.1 General [dcl.init.general]") or[*designated-initializer-clause*](dcl.init.general#nt:designated-initializer-clause "9.5.1 General [dcl.init.general]")*s* of the [*designated-initializer-list*](dcl.init.general#nt:designated-initializer-list "9.5.1 General [dcl.init.general]") are called the [*elements*](#def:elements) of the initializer list[.](#1.sentence-2) An initializer list may be empty[.](#1.sentence-3) List-initialization can occur in direct-initialization or copy-initialization contexts; list-initialization in a direct-initialization context is called[*direct-list-initialization*](#def:direct-list-initialization "9.5.5 List-initialization [dcl.init.list]") and list-initialization in a copy-initialization context is called [*copy-list-initialization*](#def:copy-list-initialization "9.5.5 List-initialization [dcl.init.list]")[.](#1.sentence-4) Direct-initialization that is not list-initialization is called[*direct-non-list-initialization*](#def:direct-non-list-initialization "9.5.5 List-initialization [dcl.init.list]")[.](#1.sentence-5) [*Note [1](#note-1)*: List-initialization can be used - [(1.1)](#1.1) as the initializer in a variable definition ([[dcl.init]](dcl.init "9.5 Initializers")), - [(1.2)](#1.2) as the initializer in a [*new-expression*](expr.new#nt:new-expression "7.6.2.8 New [expr.new]") ([[expr.new]](expr.new "7.6.2.8 New")), - [(1.3)](#1.3) in a return statement ([[stmt.return]](stmt.return "8.8.4 The return statement")), - [(1.4)](#1.4) as a [*for-range-initializer*](stmt.pre#nt:for-range-initializer "8.1 Preamble [stmt.pre]") ([[stmt.iter]](stmt.iter "8.6 Iteration statements")), - [(1.5)](#1.5) as a function argument ([[expr.call]](expr.call "7.6.1.3 Function call")), - [(1.6)](#1.6) as a template argument ([[temp.arg.nontype]](temp.arg.nontype "13.4.3 Constant template arguments")), - [(1.7)](#1.7) as a subscript ([[expr.sub]](expr.sub "7.6.1.2 Subscripting")), - [(1.8)](#1.8) as an argument to a constructor invocation ([[dcl.init]](dcl.init "9.5 Initializers"), [[expr.type.conv]](expr.type.conv "7.6.1.4 Explicit type conversion (functional notation)")), - [(1.9)](#1.9) as an initializer for a non-static data member ([[class.mem]](class.mem "11.4 Class members")), - [(1.10)](#1.10) in a [*mem-initializer*](class.base.init#nt:mem-initializer "11.9.3 Initializing bases and members [class.base.init]") ([[class.base.init]](class.base.init "11.9.3 Initializing bases and members")), or - [(1.11)](#1.11) on the right-hand side of an assignment ([[expr.assign]](expr.assign "7.6.19 Assignment and compound assignment operators"))[.](#1.sentence-6) [*Example [1](#example-1)*: int a = {1}; std::complex z{1,2};new std::vector{"once", "upon", "a", "time"}; // 4 string elements f( {"Nicholas","Annemarie"} ); // pass list of two elementsreturn { "Norah" }; // return list of one elementint* e {}; // initialization to zero / null pointer x = double{1}; // explicitly construct a double std::map anim = { {"bear",4}, {"cassowary",2}, {"tiger",7} }; — *end example*] — *end note*] [2](#2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/declarations.tex#L6413) A constructor is an [*initializer-list constructor*](#def:initializer-list_constructor "9.5.5 List-initialization [dcl.init.list]") if its first parameter is of type std​::​initializer_list or reference tocv std​::​initializer_list for some type E, and either there are no other parameters or else all other parameters have default arguments ([[dcl.fct.default]](dcl.fct.default "9.3.4.7 Default arguments"))[.](#2.sentence-1) [*Note [2](#note-2)*: Initializer-list constructors are favored over other constructors in list-initialization ([[over.match.list]](over.match.list "12.2.2.8 Initialization by list-initialization"))[.](#2.sentence-2) Passing an initializer list as the argument to the constructor template template C(T) of a class C does not create an initializer-list constructor, because an initializer list argument causes the corresponding parameter to be a non-deduced context ([[temp.deduct.call]](temp.deduct.call "13.10.3.2 Deducing template arguments from a function call"))[.](#2.sentence-3) — *end note*] The templatestd​::​initializer_list is not predefined; if a standard library declaration ([[initializer.list.syn]](initializer.list.syn "17.11.2 Header synopsis"), [[std.modules]](std.modules "16.4.2.4 Modules")) of std​::​initializer_list is not reachable from ([[module.reach]](module.reach "10.7 Reachability")) a use of std​::​initializer_list — even an implicit use in which the type is not named ([[dcl.spec.auto]](dcl.spec.auto "9.2.9.7 Placeholder type specifiers")) — the program is ill-formed[.](#2.sentence-4) [3](#3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/declarations.tex#L6433) List-initialization of an object or reference of type *cv* T is defined as follows: - [(3.1)](#3.1) If the [*braced-init-list*](dcl.init.general#nt:braced-init-list "9.5.1 General [dcl.init.general]") contains a [*designated-initializer-list*](dcl.init.general#nt:designated-initializer-list "9.5.1 General [dcl.init.general]") andT is not a reference type,T shall be an aggregate class[.](#3.1.sentence-1) The ordered [*identifier*](lex.name#nt:identifier "5.11 Identifiers [lex.name]")*s* in the [*designator*](dcl.init.general#nt:designator "9.5.1 General [dcl.init.general]")*s* of the [*designated-initializer-list*](dcl.init.general#nt:designated-initializer-list "9.5.1 General [dcl.init.general]") shall form a subsequence of the ordered [*identifier*](lex.name#nt:identifier "5.11 Identifiers [lex.name]")*s* in the direct non-static data members of T[.](#3.1.sentence-2) Aggregate initialization is performed ([[dcl.init.aggr]](dcl.init.aggr "9.5.2 Aggregates"))[.](#3.1.sentence-3) [*Example [2](#example-2)*: struct A { int x; int y; int z; }; A a{.y = 2, .x = 1}; // error: designator order does not match declaration order A b{.x = 1, .z = 2}; // OK, b.y initialized to 0 — *end example*] - [(3.2)](#3.2) If T is an aggregate class and the initializer list has a single element of type *cv1* U, where U is T or a class derived from T, the object is initialized from that element (by copy-initialization for copy-list-initialization, or by direct-initialization for direct-list-initialization)[.](#3.2.sentence-1) - [(3.3)](#3.3) Otherwise, if T is a character array and the initializer list has a single element that is an appropriately-typed [*string-literal*](lex.string#nt:string-literal "5.13.5 String literals [lex.string]") ([[dcl.init.string]](dcl.init.string "9.5.3 Character arrays")), initialization is performed as described in that subclause[.](#3.3.sentence-1) - [(3.4)](#3.4) Otherwise, if T is an aggregate, aggregate initialization is performed ([[dcl.init.aggr]](dcl.init.aggr "9.5.2 Aggregates"))[.](#3.4.sentence-1) [*Example [3](#example-3)*: double ad[] = { 1, 2.0 }; // OKint ai[] = { 1, 2.0 }; // error: narrowingstruct S2 {int m1; double m2, m3;}; S2 s21 = { 1, 2, 3.0 }; // OK S2 s22 { 1.0, 2, 3 }; // error: narrowing S2 s23 { }; // OK, default to 0,0,0 — *end example*] - [(3.5)](#3.5) Otherwise, if the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized[.](#3.5.sentence-1) - [(3.6)](#3.6) Otherwise, if T is a specialization of std​::​initializer_list, the object is constructed as described below[.](#3.6.sentence-1) - [(3.7)](#3.7) Otherwise, if T is a class type, constructors are considered[.](#3.7.sentence-1) The applicable constructors are enumerated and the best one is chosen through overload resolution ([[over.match]](over.match "12.2 Overload resolution"), [[over.match.list]](over.match.list "12.2.2.8 Initialization by list-initialization"))[.](#3.7.sentence-2) If a narrowing conversion (see below) is required to convert any of the arguments, the program is ill-formed[.](#3.7.sentence-3) [*Example [4](#example-4)*: struct S { S(std::initializer_list); // #1 S(std::initializer_list); // #2 S(std::initializer_list); // #3 S(); // #4// ...}; S s1 = { 1.0, 2.0, 3.0 }; // invoke #1 S s2 = { 1, 2, 3 }; // invoke #2 S s3{s2}; // invoke #3 (not the copy constructor) S s4 = { }; // invoke #4 — *end example*] [*Example [5](#example-5)*: struct Map { Map(std::initializer_list>);}; Map ship = {{"Sophie",14}, {"Surprise",28}}; — *end example*] [*Example [6](#example-6)*: struct S {// no initializer-list constructors S(int, double, double); // #1 S(); // #2// ...}; S s1 = { 1, 2, 3.0 }; // OK, invoke #1 S s2 { 1.0, 2, 3 }; // error: narrowing S s3 { }; // OK, invoke #2 — *end example*] - [(3.8)](#3.8) Otherwise, if T is an enumeration with a fixed underlying type ([[dcl.enum]](dcl.enum "9.8.1 Enumeration declarations")) U, the [*initializer-list*](dcl.init.general#nt:initializer-list "9.5.1 General [dcl.init.general]") has a single element v of scalar type,v can be implicitly converted to U, and the initialization is direct-list-initialization, the object is initialized with the value T(v) ([[expr.type.conv]](expr.type.conv "7.6.1.4 Explicit type conversion (functional notation)")); if a narrowing conversion is required to convert v to U, the program is ill-formed[.](#3.8.sentence-1) [*Example [7](#example-7)*: enum byte : unsigned char { }; byte b { 42 }; // OK byte c = { 42 }; // error byte d = byte{ 42 }; // OK; same value as b byte e { -1 }; // errorstruct A { byte b; }; A a1 = { { 42 } }; // error A a2 = { byte{ 42 } }; // OKvoid f(byte); f({ 42 }); // errorenum class Handle : uint32_t { Invalid = 0 }; Handle h { 42 }; // OK — *end example*] - [(3.9)](#3.9) Otherwise, if the initializer list is not a [*designated-initializer-list*](dcl.init.general#nt:designated-initializer-list "9.5.1 General [dcl.init.general]") and has a single element of type E and eitherT is not a reference type or its referenced type is reference-related to E, the object or reference is initialized from that element (by copy-initialization for copy-list-initialization, or by direct-initialization for direct-list-initialization); if a narrowing conversion (see below) is required to convert the element to T, the program is ill-formed[.](#3.9.sentence-1) [*Example [8](#example-8)*: int x1 {2}; // OKint x2 {2.0}; // error: narrowing — *end example*] - [(3.10)](#3.10) Otherwise, if T is a reference type, a prvalue is generated[.](#3.10.sentence-1) The prvalue initializes its result object by copy-list-initialization from the initializer list[.](#3.10.sentence-2) The prvalue is then used to direct-initialize the reference[.](#3.10.sentence-3) The type of the prvalue is the type referenced by T, unless T is “reference to array of unknown bound of U”, in which case the type of the prvalue is the type of x in the declaration U x[] H, where H is the initializer list[.](#3.10.sentence-4) [*Note [3](#note-3)*: As usual, the binding will fail and the program is ill-formed if the reference type is an lvalue reference to a non-const type[.](#3.10.sentence-5) — *end note*] [*Example [9](#example-9)*: struct S { S(std::initializer_list); // #1 S(const std::string&); // #2// ...};const S& r1 = { 1, 2, 3.0 }; // OK, invoke #1const S& r2 { "Spinach" }; // OK, invoke #2 S& r3 = { 1, 2, 3 }; // error: initializer is not an lvalueconst int& i1 = { 1 }; // OKconst int& i2 = { 1.1 }; // error: narrowingconst int (&iar)[2] = { 1, 2 }; // OK, iar is bound to temporary arraystruct A { } a;struct B { explicit B(const A&); };const B& b2{a}; // error: cannot copy-list-initialize B temporary from Astruct C { int x; }; C&& c = { .x = 1 }; // OK — *end example*] - [(3.11)](#3.11) Otherwise, if the initializer list has no elements, the object is value-initialized[.](#3.11.sentence-1) [*Example [10](#example-10)*: int** pp {}; // initialized to null pointer — *end example*] - [(3.12)](#3.12) Otherwise, the program is ill-formed[.](#3.12.sentence-1) [*Example [11](#example-11)*: struct A { int i; int j; }; A a1 { 1, 2 }; // aggregate initialization A a2 { 1.2 }; // error: narrowingstruct B { B(std::initializer_list);}; B b1 { 1, 2 }; // creates initializer_list and calls constructor B b2 { 1, 2.0 }; // error: narrowingstruct C { C(int i, double j);}; C c1 = { 1, 2.2 }; // calls constructor with arguments (1, 2.2) C c2 = { 1.1, 2 }; // error: narrowingint j { 1 }; // initialize to 1int k { }; // initialize to 0 — *end example*] [4](#4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/declarations.tex#L6651) Within the [*initializer-list*](dcl.init.general#nt:initializer-list "9.5.1 General [dcl.init.general]") of a [*braced-init-list*](dcl.init.general#nt:braced-init-list "9.5.1 General [dcl.init.general]"), the [*initializer-clause*](dcl.init.general#nt:initializer-clause "9.5.1 General [dcl.init.general]")*s*, including any that result from pack expansions ([[temp.variadic]](temp.variadic "13.7.4 Variadic templates")), are evaluated in the order in which they appear[.](#4.sentence-1) That is, every value computation and side effect associated with a given [*initializer-clause*](dcl.init.general#nt:initializer-clause "9.5.1 General [dcl.init.general]") is sequenced before every value computation and side effect associated with any [*initializer-clause*](dcl.init.general#nt:initializer-clause "9.5.1 General [dcl.init.general]") that follows it in the comma-separated list of the [*initializer-list*](dcl.init.general#nt:initializer-list "9.5.1 General [dcl.init.general]")[.](#4.sentence-2) [*Note [4](#note-4)*: This evaluation ordering holds regardless of the semantics of the initialization; for example, it applies when the elements of the[*initializer-list*](dcl.init.general#nt:initializer-list "9.5.1 General [dcl.init.general]") are interpreted as arguments of a constructor call, even though ordinarily there are no sequencing constraints on the arguments of a call[.](#4.sentence-3) — *end note*] [5](#5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/declarations.tex#L6667) An object of type std​::​initializer_list is constructed from an initializer list as if the implementation generated and materialized ([[conv.rval]](conv.rval "7.3.5 Temporary materialization conversion")) a prvalue of type “array of N const E”, where N is the number of elements in the initializer list; this is called the initializer list's [*backing array*](#def:array,backing "9.5.5 List-initialization [dcl.init.list]")[.](#5.sentence-1) Each element of the backing array is copy-initialized with the corresponding element of the initializer list, and thestd​::​initializer_list object is constructed to refer to that array[.](#5.sentence-2) [*Note [5](#note-5)*: A constructor or conversion function selected for the copy needs to be accessible ([[class.access]](class.access "11.8 Member access control")) in the context of the initializer list[.](#5.sentence-3) — *end note*] If a narrowing conversion is required to initialize any of the elements, the program is ill-formed[.](#5.sentence-4) [*Note [6](#note-6)*: Backing arrays are potentially non-unique objects ([[intro.object]](intro.object "6.8.2 Object model"))[.](#5.sentence-5) — *end note*] [6](#6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/declarations.tex#L6687) The backing array has the same lifetime as any other temporary object ([[class.temporary]](class.temporary "6.8.7 Temporary objects")), except that initializing aninitializer_list object from the array extends the lifetime of the array exactly like binding a reference to a temporary[.](#6.sentence-1) [*Example [12](#example-12)*: void f(std::initializer_list il);void g(float x) { f({1, x, 3});}void h() { f({1, 2, 3});}struct A {mutable int i;};void q(std::initializer_list);void r() { q({A{1}, A{2}, A{3}});} The initialization will be implemented in a way roughly equivalent to this:void g(float x) {const double __a[3] = {double{1}, double{x}, double{3}}; // backing array f(std::initializer_list(__a, __a+3));}void h() {static constexpr double __b[3] = {double{1}, double{2}, double{3}}; // backing array f(std::initializer_list(__b, __b+3));}void r() {const A __c[3] = {A{1}, A{2}, A{3}}; // backing array q(std::initializer_list(__c, __c+3));} assuming that the implementation can construct an initializer_list object with a pair of pointers, and with the understanding that __b does not outlive the call to f[.](#6.sentence-2) — *end example*] [*Example [13](#example-13)*: typedef std::complex cmplx; std::vector v1 = { 1, 2, 3 }; void f() { std::vector v2{ 1, 2, 3 }; std::initializer_list i3 = { 1, 2, 3 };}struct A { std::initializer_list i4; A() : i4{ 1, 2, 3 } {} // ill-formed, would create a dangling reference}; For v1 and v2, the initializer_list object is a parameter in a function call, so the array created for{ 1, 2, 3 } has full-expression lifetime[.](#6.sentence-3) For i3, the initializer_list object is a variable, so the array persists for the lifetime of the variable[.](#6.sentence-4) For i4, the initializer_list object is initialized in the constructor's [*ctor-initializer*](class.base.init#nt:ctor-initializer "11.9.3 Initializing bases and members [class.base.init]") as if by binding a temporary array to a reference member, so the program is ill-formed ([[class.base.init]](class.base.init "11.9.3 Initializing bases and members"))[.](#6.sentence-5) — *end example*] [7](#7) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/declarations.tex#L6758) A [*narrowing conversion*](#def:conversion,narrowing "9.5.5 List-initialization [dcl.init.list]") is an implicit conversion - [(7.1)](#7.1) from a floating-point type to an integer type, or - [(7.2)](#7.2) from a floating-point type T to another floating-point type whose floating-point conversion rank is neither greater than nor equal to that of T, except where the result of the conversion is a constant expression and either its value is finite and the conversion did not overflow, or the values before and after the conversion are not finite, or - [(7.3)](#7.3) from an integer type or unscoped enumeration type to a floating-point type, except where the source is a constant expression and the actual value after conversion will fit into the target type and will produce the original value when converted back to the original type, or - [(7.4)](#7.4) from an integer type or unscoped enumeration type to an integer type that cannot represent all the values of the original type, except where * [(7.4.1)](#7.4.1) the source is a bit-field whose width w is less than that of its type (or, for an enumeration type, its underlying type) and the target type can represent all the values of a hypothetical extended integer type with width w and with the same signedness as the original type or * [(7.4.2)](#7.4.2) the source is a constant expression whose value after integral promotions will fit into the target type, or - [(7.5)](#7.5) from a pointer type or a pointer-to-member type to bool[.](#7.sentence-1) [*Note [7](#note-7)*: As indicated above, such conversions are not allowed at the top level in list-initializations[.](#7.sentence-2) — *end note*] [*Example [14](#example-14)*: int x = 999; // x is not a constant expressionconst int y = 999;const int z = 99;char c1 = x; // OK, though it potentially narrows (in this case, it does narrow)char c2{x}; // error: potentially narrowschar c3{y}; // error: narrows (assuming char is 8 bits)char c4{z}; // OK, no narrowing neededunsigned char uc1 = {5}; // OK, no narrowing neededunsigned char uc2 = {-1}; // error: narrowsunsigned int ui1 = {-1}; // error: narrowssigned int si1 ={ (unsigned int)-1 }; // error: narrowsint ii = {2.0}; // error: narrowsfloat f1 { x }; // error: potentially narrowsfloat f2 { 7 }; // OK, 7 can be exactly represented as a floatbool b = {"meow"}; // error: narrowsint f(int);int a[] = { 2, f(2), f(2.0) }; // OK, the double-to-int conversion is not at the top level — *end example*]