[dcl.init.list] (original) (raw)

9 Declarations [dcl]

9.5 Initializers [dcl.init]

9.5.5 List-initialization [dcl.init.list]

An initializer list may be empty.

List-initialization can occur in direct-initialization or copy-initialization contexts; list-initialization in a direct-initialization context is calleddirect-list-initialization and list-initialization in a copy-initialization context is called copy-list-initialization.

Direct-initialization that is not list-initialization is calleddirect-non-list-initialization.

[Note 1:

List-initialization can be used

[Example 1: int a = {1}; std::complex<double> z{1,2};new std::vectorstd::string\{"once", "upon", "a", "time"}; f( {"Nicholas","Annemarie"} ); return { "Norah" }; int* e {}; x = double{1}; std::mapstd::string,int\ anim = { {"bear",4}, {"cassowary",2}, {"tiger",7} }; — _end example_]

— _end note_]

A constructor is an initializer-list constructor if its first parameter is of type std​::​initializer_list<E> or reference tocv std​::​initializer_list<E> for some type E, and either there are no other parameters or else all other parameters have default arguments ([dcl.fct.default]).

[Note 2:

Initializer-list constructors are favored over other constructors in list-initialization ([over.match.list]).

Passing an initializer list as the argument to the constructor template template<class T> 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]).

— _end note_]

The templatestd​::​initializer_list is not predefined; if a standard library declaration ([initializer.list.syn], [std.modules]) of std​::​initializer_list is not reachable from ([module.reach]) a use of std​::​initializer_list — even an implicit use in which the type is not named ([dcl.spec.auto]) — the program is ill-formed.

List-initialization of an object or reference of type cv T is defined as follows:

That is, every value computation and side effect associated with a given initializer-clause is sequenced before every value computation and side effect associated with any initializer-clausethat follows it in the comma-separated list of the initializer-list.

[Note 4:

This evaluation ordering holds regardless of the semantics of the initialization; for example, it applies when the elements of theinitializer-list are interpreted as arguments of a constructor call, even though ordinarily there are no sequencing constraints on the arguments of a call.

— _end note_]

An object of type std​::​initializer_list<E> is constructed from an initializer list as if the implementation generated and materialized ([conv.rval]) 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.

Each element of the backing array is copy-initialized with the corresponding element of the initializer list, and thestd​::​initializer_list<E> object is constructed to refer to that array.

[Note 5:

A constructor or conversion function selected for the copy needs to be accessible ([class.access]) in the context of the initializer list.

— _end note_]

If a narrowing conversion is required to initialize any of the elements, the program is ill-formed.

[Note 6:

Backing arrays are potentially non-unique objects ([intro.object]).

— _end note_]

The backing array has the same lifetime as any other temporary object ([class.temporary]), except that initializing aninitializer_list object from the array extends the lifetime of the array exactly like binding a reference to a temporary.

[Example 12: void f(std::initializer_list<double> 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<A>);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}}; f(std::initializer_list<double>(__a, __a+3));} void h() { static constexpr double __b[3] = {double{1}, double{2}, double{3}}; f(std::initializer_list<double>(__b, __b+3));} void r() { const A __c[3] = {A{1}, A{2}, A{3}}; q(std::initializer_list<A>(__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.

— _end example_]

[Example 13: typedef std::complex<double> cmplx; std::vector<cmplx> v1 = { 1, 2, 3 };void f() { std::vector<cmplx> v2{ 1, 2, 3 }; std::initializer_list<int> i3 = { 1, 2, 3 };} struct A { std::initializer_list<int> i4; A() : i4{ 1, 2, 3 } {} };

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.

For i3, the initializer_list object is a variable, so the array persists for the lifetime of the variable.

For i4, the initializer_list object is initialized in the constructor's ctor-initializer as if by binding a temporary array to a reference member, so the program is ill-formed ([class.base.init]).

— _end example_]

A narrowing conversion is an implicit conversion

[Note 7:

As indicated above, such conversions are not allowed at the top level in list-initializations.

— _end note_]

[Example 14: int x = 999; const int y = 999;const int z = 99;char c1 = x; char c2{x}; char c3{y}; char c4{z}; unsigned char uc1 = {5}; unsigned char uc2 = {-1}; unsigned int ui1 = {-1}; signed int si1 = { (unsigned int)-1 }; int ii = {2.0}; float f1 { x }; float f2 { 7 }; bool b = {"meow"}; int f(int);int a[] = { 2, f(2), f(2.0) }; — _end example_]