List-initialization is initialization of an object or reference from a braced-init-list. Such an initializer is called an initializer list, and the comma-separated initializer-clauses of the list are called the elements of the initializer 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 called direct-list-initialization and list-initialization in a copy-initialization context is called copy-list-initialization. [ Note: List-initialization can be used
as the initializer in a variable definition ([dcl.init])
as the initializer in a new expression ([expr.new])
in a return statement ([stmt.return])
as a for-range-initializer ([stmt.iter])
as a function argument ([expr.call])
as a subscript ([expr.sub])
as an argument to a constructor invocation ([dcl.init], [expr.type.conv])
as an initializer for a non-static data member ([class.mem])
in a mem-initializer ([class.base.init])
on the right-hand side of an assignment ([expr.ass])
[ Example:
int a = {1}; std::complex<double> z{1,2}; new std::vector<std::string>{"once", "upon", "a", "time"}; f( {"Nicholas","Annemarie"} ); return { "Norah" }; int* e {}; x = double{1}; std::map<std::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 to possibly cv-qualified 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: 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 template std::initializer_list is not predefined; if the header <initializer_list> is not included prior to 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 T is defined as follows:
If T is an aggregate, aggregate initialization is performed ([dcl.init.aggr]).
[ Example:
double ad[] = { 1, 2.0 }; int ai[] = { 1, 2.0 }; struct S2 { int m1; double m2, m3; }; S2 s21 = { 1, 2, 3.0 }; S2 s22 { 1.0, 2, 3 }; S2 s23 { };
— end example ]
Otherwise, if the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized.
Otherwise, if T is a specialization of std::initializer_list<E>, a prvalue initializer_list object is constructed as described below and used to initialize the object according to the rules for initialization of an object from a class of the same type ([dcl.init]).
Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution ([over.match], [over.match.list]). If a narrowing conversion (see below) is required to convert any of the arguments, the program is ill-formed.
[ Example:
struct S { S(std::initializer_list<double>); S(std::initializer_list<int>); S(); }; S s1 = { 1.0, 2.0, 3.0 }; S s2 = { 1, 2, 3 }; S s3 = { };
— end example ]
[ Example:
struct Map { Map(std::initializer_list<std::pair<std::string,int>>); }; Map ship = {{"Sophie",14}, {"Surprise",28}};
— end example ]
[ Example:
struct S { S(int, double, double); S(); }; S s1 = { 1, 2, 3.0 }; S s2 { 1.0, 2, 3 }; S s3 { };
— end example ]
Otherwise, if the initializer list has a single element of type E and either T is not a reference type or its referenced type is reference-related to E, the object or reference is initialized from that element; if a narrowing conversion (see below) is required to convert the element to T, the program is ill-formed.
[ Example:
int x1 {2}; int x2 {2.0};
— end example ]
Otherwise, if T is a reference type, a prvalue temporary of the type referenced by T is copy-list-initialized or direct-list-initialized, depending on the kind of initialization for the reference, and the reference is bound to that temporary. [ Note: 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. — end note ]
[ Example:
struct S { S(std::initializer_list<double>); S(const std::string&); }; const S& r1 = { 1, 2, 3.0 }; const S& r2 { "Spinach" }; S& r3 = { 1, 2, 3 }; const int& i1 = { 1 }; const int& i2 = { 1.1 }; const int (&iar)[2] = { 1, 2 };
— end example ]
Otherwise, if the initializer list has no elements, the object is value-initialized.
[ Example:
int** pp {};
— end example ]
Otherwise, the program is ill-formed.
[ Example:
struct A { int i; int j; }; A a1 { 1, 2 }; A a2 { 1.2 }; struct B { B(std::initializer_list<int>); }; B b1 { 1, 2 }; B b2 { 1, 2.0 }; struct C { C(int i, double j); }; C c1 = { 1, 2.2 }; C c2 = { 1.1, 2 }; int j { 1 }; int k { };
— end example ]
Within the initializer-list of a braced-init-list, the initializer-clauses, including any that result from pack expansions ([temp.variadic]), are evaluated in the order in which they appear. 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-clause that follows it in the comma-separated list of the initializer-list. [ Note: This evaluation ordering holds regardless of the semantics of the initialization; for example, it applies when the elements of the initializer-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 allocated a temporary array of N elements of type const E, where N is the number of elements in the initializer list. Each element of that array is copy-initialized with the corresponding element of the initializer list, and the std::initializer_list<E> object is constructed to refer to that array. [ Note: A constructor or conversion function selected for the copy shall be accessible (Clause [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.[ Example:
struct X { X(std::initializer_list<double> v); }; X x{ 1,2,3 };
The initialization will be implemented in a way roughly equivalent to this:
const double __a[3] = {double{1}, double{2}, double{3}}; X x(std::initializer_list<double>(__a, __a+3));
assuming that the implementation can construct an initializer_list object with a pair of pointers. — end example ]
The array has the same lifetime as any other temporary object ([class.temporary]), except that initializing an initializer_list object from the array extends the lifetime of the array exactly like binding a reference to a temporary. [ Example:
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 a constructor's ctor-initializer, so the array persists only until the constructor exits, and so any use of the elements of i4 after the constructor exits produces undefined behavior. — end example ] [ Note: The implementation is free to allocate the array in read-only memory if an explicit array with the same initializer could be so allocated. — end note ]
A narrowing conversion is an implicit conversion
from a floating-point type to an integer type, or
from long double to double or float, or from double to float, except where the source is a constant expression and the actual value after conversion is within the range of values that can be represented (even if it cannot be represented exactly), or
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
from an integer type or unscoped enumeration type to an integer type that cannot represent all the values of the original type, except where the source is a constant expression whose value after integral promotions will fit into the target type.
[ Note: As indicated above, such conversions are not allowed at the top level in list-initializations. — end note ] [ Example:
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 }; int f(int); int a[] = { 2, f(2), f(2.0) };
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4