The changes to clauses 18 and 20 are from N2531=08-0041; the LWG felt that they belong with the core wording.
In 8.5 [dcl.init], change
initializer: = initializer-clause ( expression-list ) braced-init-list initializer-clause: assignment-expression{ initializer-list ,opt}{ }braced-init-list initializer-list: initializer-clause ...opt initializer-list , initializer-clause ...opt braced-init-list: { initializer-list ,opt } { }
In 5.2 [expr.post], change
postfix-expression: ... postfix-expression [ expression ] postfix-expression [ braced-init-list ] postfix-expression ( expression-listopt ) simple-type-specifier ( expression-listopt ) typename-specifier ( expression-listopt ) simple-type-specifier braced-init-list typename-specifier braced-init-list ... expression-list:assignment-expression ...optexpression-list , assignment-expression ...optinitializer-list
In 5.3.4 [expr.new], change
new-initializer: ( expression-listopt ) braced-init-list
In 5.17 [expr.ass], change
assignment-expression: conditional-expressionlogical-or-expression assignment-operator assignment-expressionlogical-or-expression assignment-operator initializer-clause throw-expression
In 6.4 [stmt.select], change
condition: expression type-specifier-seq declarator =assignment-expressioninitializer-clause type-specifier-seq declarator braced-init-list
In 6.6 [stmt.jump], change
jump-statement: ... return expressionopt ; return braced-init-list ; ...
In 12.6.2 [class.base.init], change
mem-initializer: mem-initializer-id ( expression-listopt ) mem-initializer-id braced-init-list
In 8.5 [dcl.init], change paragraphs 12-15:
The initialization that occurs in the formT x = a;as well as in argument passing, function return, throwing an exception (15.1), handling an exception (15.3), andbrace-enclosed initializer listsaggregate member initialization (8.5.1) is called copy-initialization.and is equivalent to the formT x = a;The initialization that occurs in the forms
T x(a); T x{a};as well as innew
expressions (5.3.4),static_cast
expressions (5.2.9), functional notation type conversions (5.2.3), and base and member initializers (12.6.2) is called direct-initialization.and is equivalent to the formT x(a);
If T is a scalar type, then a declaration of the form
T x = { a };is equivalent to
T x = a;
The semantics of initializers are as follows. The destination type is the type of the object or reference being initialized and the source type is the type of the initializer expression. The source type is not defined when the initializer is
brace-encloseda braced-init-list or when it is a parenthesized list of expressions.
- If the destination type is a reference type, see 8.5.3.
- If the destination type is an array of characters, an array of char16_t, an array of char32_t, or an array of wchar_t, and the initializer is a string literal, see 8.5.2.
- If the initializer is a braced-init-list, the object is list-initialized ([dcl.init.list]).
- If the initializer is (), the object is value-initialized.
- Otherwise, if the destination type is an array,
see 8.5.1the program is ill-formed.- If the destination type is a (possibly cv-qualified) class type:
If the class is an aggregate (8.5.1), and the initializer is a brace-enclosed list, see 8.5.1.- If the initialization is direct-initialization, ...
In 8.5.1 [decl.init.aggr] paragraph 2, change
When an aggregate is initialized the initializer can contain an initializer-clause consisting of a brace-enclosed , comma-separated list of initializer-clauseWhen, as specified in [dcl.init.list], an aggregate is initialized by an initializer list, the elements of the initializer list are taken as initializers for the members of the aggregate,writtenin increasing subscript or member order.If the aggregate contains subaggregates, this rule applies recursively to the members of the subaggregate.Each member is copy-initialized from the corresponding initializer-clause. If the initializer-clause is an expression and a narrowing conversion ([dcl.init.list]) is required to convert the expression, the program is ill-formed. [Note: If an initializer-clause is itself an initializer list, the member is list-initialized, which will result in a recursive application of the rules in this section if the member is an aggregate. --end note] [ Example: ...
In 8.5.1 paragraph 11:
In a declaration of the formT x = { a };bBraces can be elided in an initializer-list as follows. [Footnote: Braces cannot be elided in other uses of list-initialization.] If the initializer-list begins with a left brace, ....
In 8.5 [dcl.init], add a new section as 8.5.4 [dcl.init.list]:
8.5.4 List-initialization [dcl.init.list]
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 (8.5 [dcl.init])
- as the initializer in a new expression (5.3.4 [expr.new])
- in a return statement (6.6.3 [stmt.return])
- as a function argument (5.2.2 [expr.call])
- as a subscript (5.2.1 [expr.sub])
- as an argument to a constructor invocation (8.5 [dcl.init], 5.2.3 [expr.type.conv])
- as a base-or-member initializer (12.6.2 [class.base.init])
- on the right-hand side of an assignment (5.17)
[ Example:
int a = {1}; std::complex<double> z{1,2}; new std::vector<std::string>{"once", "upon", "a", "time"}; // 4 string elements f( {"Nicholas","Annemarie"} ); // pass list of two elements return { "Norah" }; // return list of one element int* e {}; // initialization to zero / null pointer x = double{1}; // explicitly construct a double 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 (8.3.6). [Note: Initializer-list constructors are favored over other constructors in list-initialization ([over.match.list]).] 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 (8.5.1 [dcl.init.aggr]). [Example:
double ad[] = { 1, 2.0 }; // ok int ai[] = { 1, 2.0 }; // error: narrowing--- end example]
- Otherwise, if T is a specialization of std::initializer_list<E>, an 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 (8.5).
Otherwise, if T is a class type, constructors are considered. If T has an initializer-list constructor, the argument list consists of the initializer list as a single argument; otherwise, the argument list consists of the elements of the initializer list. The applicable constructors are enumerated ([over.match.list]) and the best one is chosen through overload resolution (13.3). 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>); // #1 S(std::initializer_list<int>); // #2 // ... }; S s1 = { 1.0, 2.0, 3.0 }; // invoke #1 S s2 = { 1, 2, 3 }; // invoke #2--- 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 { // no initializer-list constructors S(int, double, double); // #2 S(); // #3 // ... }; S s1 = { 1, 2, 3.0 }; // ok: invoke #2 S s2 { 1.0, 2, 3 }; // error: narrowing S s3 { }; // ok: invoke #3 struct 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]
Otherwise, if T is a reference type, an rvalue temporary of the type referenced by T is list-initialized, 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. ]
[ Example:
struct S { S(std::initializer_list<double>); // #1 S(const std::string&); // #2 // ... }; const S& r1 = { 1, 2, 3.0 }; // ok: invoke #1 const S& r2 { "Spinach" }; // ok: invoke #2 S& r3 = { 1, 2, 3 }; // error: initializer is not an lvalue--- end example]
Otherwise (i.e., if T is not an aggregate, class type, or reference), if the initializer list has a single element, the object 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}; // ok int x2 {2.0}; // error: narrowing--- end example]
Otherwise, if the initializer list has no elements, the object is value-initialized; [ Example
int** pp {}; // initialized to null pointer--- end example]
Otherwise, the program is ill-formed.
[ Example:
struct A { int i; int j; }; A a1 { 1, 2 }; // aggregate initialization A a2 { 1.2 }; // error: narrowing struct B { B(std::initializer_list<int>); }; B b1 { 1, 2 }; // creates initializer_list<int> and calls constructor B b2 { 1, 2.0 }; // error: narrowing struct C { C(int i, double j); }; C c1 = { 1, 2.2 }; // calls constructor with arguments (1, 2.2) C c2 = { 1.1, 2 }; // error: narrowing int j { 1 }; // initialize to 1 int k {}; // initialize to 0--- end example]
When an initializer list is implicitly converted to a std::initializer_list<E>, the object passed is constructed as if the implementation allocated an array of N elements of type E, where N is the number of elements in the initializer list. Each element of that array is initialized with the corresponding element of the initializer list converted to E, and the std::initializer_list<E> object is constructed to refer to that array. If a narrowing conversion is required to convert the element to E, 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:
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 with a pair of pointers. --- end example]
The lifetime of the array is the same as that of the initializer_list object. [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 }; }For v1 and v2, the initializer_list object and array created for { 1, 2, 3 } have full-expression lifetime. For i3, the initializer_list object and array have automatic lifetime. --- 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 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 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 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.
[Note: As indicated above, such conversions are not allowed at the top level in list-initializations. [ Example:
int x = 999; // x is not a constant expression const int y = 999; const int z = 99; char c1 = x; // ok, though it might narrow (in this case, it does narrow) char c2{x}; // error, might narrow char c3{y}; // error: narrows char c4{z}; // ok, no narrowing needed unsigned char uc1 = {5}; // ok: no narrowing needed unsigned char uc2 = {-1}; // error: narrows unsigned int ui1 = {-1}; // error: narrows signed int si1 = { (unsigned int)-1 }; // error: narrows int ii = {2.0}; // error: narrows float f1 { x }; // error: narrows float f2 { 7 }; // ok: 7 can be exactly represented as a float int f(int); int a[] = { 2, f(2), f(2.0) }; // ok: the double-to-int conversion is not at the top level--- end example] ]
In 5.2.1 [expr.sub], add as a new paragraph 2:
A braced-init-list may appear as a subscript for a user-defined operator[]. In that case, the initializer list is treated as the initializer for the subscript argument of the operator[]. An initializer list shall not be used with the built-in subscript operator. [ Example:
struct X { Z operator[](std::initializer_list<int>); }; X x; x[{1,2,3}] = 7; // ok: meaning x.operator[]({1,2,3}) int a[10]; a[{1,2,3}] = 7; // error: built-in subscript operator--- end example]
In 5.2.3 [expr.type.conv], change:
A simple-type-specifier (7.1.6.2) or typename-specifier (14.6) followed by a parenthesized expression-list constructs a value of the specified type given the expression list. If the expression list is a single expression, the type conversion expression is equivalent (in definedness, and if defined in meaning) to the corresponding cast expression (5.4). If the
simple-type-specifier specifiestype specified is a class type, the class type shall be complete. If the expression list specifies more than a single value, the type shall be a class with a suitably declared constructor (8.5, 12.1), and the expression T(x1, x2, ...) is equivalent in effect to the declaration T t(x1, x2, ...); for some invented temporary variable t, with the result being the value of t as an rvalue.The expression T(), where T is a simple-type-specifier
(7.1.6.2)or typename-specifier for a non-array complete object type or the (possibly cv-qualified) void type, creates an rvalue of the specified type, which is value-initialized (8.5; no initialization is done for the void() case). [ Note: if T is a non-class type that is cv-qualified, the cv-qualifiers are ignored when determining the type of the resulting rvalue (3.10). --- end note ]Similarly, a simple-type-specifier or typename-specifier followed by a braced-init-list creates a temporary object of the specified type direct-list-initialized ([dcl.init.list]) with the specified braced-init-list, and its value is that temporary object as an rvalue.
In 5.3.4 [expr.new] paragraph 16, change part of the bullet list:
- ...
If the new-initializer is of the form (), the item is value-initialized (8.5);If the new-initializer is of the form (expression-list) and T is a class type, the appropriate constructor is called, using expression-list as the arguments (8.5);If the new-initializer is of the form (expression-list ) and T is an arithmetic, enumeration, pointer, or pointer-to-member type and expression-list comprises exactly one expression, then the object is initialized to the (possibly converted) value of the expression (8.5);Otherwise the new-expression is ill-formed.- Otherwise, the new-initializer is interpreted according to the initialization rules of 8.5 [dcl.init] for direct-initialization.
In 5.17 [expr.ass], add as a new final paragraph:
A braced-init-list may appear on the right-hand side of
an assignment to a scalar, in which case the initializer list must have at most a single element. The meaning of x={v}, where T is the scalar type of the expression x, is that of x=T(v) except that no narrowing conversion ([dcl.init.list]) is allowed. The meaning of x={} is x=T().
an assignment defined by a user-defined assignment operator, in which case the initializer list is passed as the argument to the operator function.
[Example:
complex<double> z; z = { 1,2 }; // meaning z.operator=({1,2}) z += { 1, 2 }; // meaning z.operator+=({1,2}) a = b = { 1 }; // meaning a=b=1; a = { 1 } = b; // syntax error--- end example]
In 6.4 paragraph 2:
... If the auto type-specifier appears in the type-specifier-seq, the type-specifier-seq shall contain no other type-specifiers except cv-qualifiers, and the type of the identifier being declared is deduced from theassignment-expressioninitializer as described in 7.1.6.4.
In 6.6.3 [stmt.return] paragraph 2, change
A return statement without an expression can be used only in functions that do not return a value, that is, a function with the return type void, a constructor (12.1), or a destructor (12.4). A return statement with an expression of non-void type can be used only in functions returning a value; the value of the expression is returned to the caller of the function. The expression is implicitly converted to the return type of the function in which it appears. A return statement can involve the construction and copy of a temporary object (12.2). [ Note: A copy operation associated with a return statement may be elided or considered as an rvalue for the purpose of overload resolution in selecting a constructor (12.8). -- end note ] A return statement with a braced-init-list initializes the object or reference to be returned from the function by copy-list-initialization ([dcl.init.list]) from the specified initializer list. [ Example:
std::pair<std::string,int> f(const char* p, int x) { return {p,x}; }--- end example]
Flowing off the end of a function is equivalent to a return with no value; this results in undefined behavior in a value-returning function.
In 7.1.6.4 [dcl.spec.auto], paragraph 6, change
Once the type of a declarator-id has been determined according to 8.3, the type of the declared variable using the declarator-id is determined from the type of its initializer using the rules for template argument deduction. Let T be the type that has been determined for a variable identifier d. Obtain P from T by replacing the occurrences of auto with either a new invented type template parameter U or, if the initializer is a braced-init-list ([dcl.init.list]), with std::initializer_list<U>.
Let A be the type of the initializer expression for d.The type deduced for the variable d is then the deduced type determined using the rules of template argument deduction from a function call (14.8.2.1), where P is a function template parameter type andAthe initializer for d is the corresponding argumenttype. If the deduction fails, the declaration is ill-formed. [ Example:auto x1 = { 1, 2 }; // decltype(x1) is std::initializer_list<int> auto x2 = { 1, 2.0 }; // error: cannot deduce element type--- end example]
In 12.2 [class.temporary], paragraph 3, change
The second context is when a reference is bound to a temporary. The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except as specified below. A temporary bound to a reference member in a constructor's ctor-initializer (12.6.2) persists until the constructor exits. A temporary bound to a reference parameter in a function call (5.2.2) persists until the completion of the full expression containing the call. A temporary bound to the returned value in a function return statement (6.6.3) persists until the function exits. A temporary bound to a reference in a new-initializer (5.3.4 [expr.new]) persists until the completion of the full-expression containing the new-initializer [Example:
struct S { int mi; const std::pair<int,int>& mp; }; S a { 1, {2,3} }; S* p = new S{ 1, {2,3} }; // Creates dangling reference--- end example] [ Note: This may introduce a dangling reference, and implementations are encouraged to issue a warning in such a case. --- end note] The destruction of a temporary whose lifetime is not extended by being bound to a reference is sequenced before the destruction of every temporary which is constructed earlier in the same full-expression. ...
In 12.3.1 [class.conv.ctor] paragraph 1, change
A constructor declared without the function-specifier explicit
that can be called with a single parameterspecifies a conversion from thetype of its first parametertypes of its parameters to the type of its class. Such a constructor is called a converting constructor.
In 12.6.1 [class.expl.init] paragraph 2, change
When an aggregate (whether class or array) contains members of class type and is initialized by a brace-enclosed initializer-list (8.5.1), each such member is copy-initialized (see 8.5) by the corresponding assignment-expression. If there are fewer initializers in the initializer-list than members of the aggregate, each member not explicitly initialized shall be value-initialized (8.5). [ Note: 8.5.1 describes how assignment-expressions in an initializer-list are paired with the aggregate members they initialize. --end note ]An object of class type can also be initialized by a braced-init-list. List-initialization semantics apply; see 8.5 [dcl.init] and [dcl.init.list]. [ Example: ...
In 12.6.2 [class.base.init] paragraph 3, change
The expression-list or braced-init-list in a mem-initializer is used to initialize the base class or non-static data member subobject denoted by the mem-initializer-id according to the initialization rules of 8.5 [dcl.init] for direct-initialization.
The semantics of a mem-initializer are as follows:
if the expression-list of the mem-initializer is omitted, the base class or member subobject is value-initialized (see 8.5);
otherwise, the subobject indicated by mem-initializer-id is direct-initialized using expression-list as the initializer (see 8.5).
In 13.3.1p6, change
Because other than in list-initialization only one user-defined conversion is allowed in an implicit conversion sequence, special rules apply when selecting the best user-defined conversion (13.3.3, 13.3.3.1).
Add a new section 13.3.1.7:
13.3.1.7 Initialization by list-initialization [over.match.list]
When objects of non-aggregate class type are list-initialized ([dcl.init.list]), overload resolution selects the constructor as follows:
If T has an initializer-list constructor ([dcl.init.list]), the argument list consists of the initializer list as a single argument; otherwise, the argument list consists of the elements of the initializer list.
For direct-list-initialization, the candidate functions are all the constructors of the class of the object being initialized.
For copy-list-initialization, the candidate functions are all the constructors of that class. However, if an explicit constructor is chosen, the initialization is ill-formed. [Note: This restriction only applies if this initialization is part of the final result of overload resolution --end note]
In 13.3.3.1p4, change
However, when considering the argument of a user-defined conversion function that is a candidate by 13.3.1.3 when invoked for the copying of the temporary in the second step of a class copy-initialization, by [over.match.list] when passing the initializer list as a single argument or when the initializer list has exactly one element and a conversion to some class X or reference to (possibly cv-qualified) X is considered for the first parameter of a constructor of X, or by 13.3.1.4, 13.3.1.5, or 13.3.1.6 in all cases, only standard conversion sequences and ellipsis conversion sequences are allowed.
Add a new section under 13.3.3.1 [over.best.ics]:
13.3.3.1.5 List-initialization sequence [over.ics.list]
When an argument is an initializer list ([dcl.init.list]), it is not an expression and special rules apply for converting it to a parameter type.
If the parameter type is std::initializer_list<X> and all the elements of the initializer list can be implicitly converted to X, the implicit conversion sequence is the worst conversion necessary to convert an element of the list to X. This conversion can be a user-defined conversion even in the context of a call to an initializer-list constructor. [ Example:
void f(std::initializer_list<int>); f( {1,2,3} ); // ok: f(initializer_list<int>) identity conversion f( {'a','b'} ); // ok: f(initializer_list<int>) integral promotion f( {1.0} ); // error: narrowing struct A { A(std::initializer_list<double>); // #1 A(std::initializer_list<complex<double>>); // #2 A(std::initializer_list<std::string>); // #3 }; A a{ 1.0,2.0 }; // ok, uses #1 void g(A); f({ "foo", "bar" }); // ok, uses #3--- end example]
Otherwise, if the parameter is a non-aggregate class X and overload resolution per [over.match.list] chooses a single best constructor of X to perform the initialization of an object of type X from the argument initializer list, the implicit conversion sequence is a user-defined conversion sequence. If multiple constructors are viable but none is better than the others, the implicit conversion sequence is the ambiguous conversion sequence. User-defined conversions are allowed for conversion of the initializer list elements to the constructor parameter types except as noted in 13.3.3.1. [ Example:
struct A { A(std::initializer_list<int>); }; void f(A); f( {'a', 'b'} ); // ok: f(A(std::initializer_list<int>)) user-defined conversion struct B { A(int, double); }; void g(B); g( {'a', 'b'} ); // ok: g(B(int,double)) user-defined conversion g( {1.0, 1,0} ); // error: narrowing void f(B); f( {'a', 'b'} ); // error: ambiguous f(A) or f(B) struct C { C(std::string); }; void h(C); h({"foo"}); // ok: h(C(std::string("foo"))) struct D { C(A, C); }; void i(D); i({ {1,2}, {"bar"} }); // ok: i(D(A(std::initializer_list<int>{1,2}),C(std::string("bar"))))--- end example]
Otherwise, if the parameter has an aggregate type which can be initialized from the initializer list according to the rules for aggregate initialization (8.5.1 [dcl.init.aggr]), the implicit conversion sequence is a user-defined conversion sequence. [ Example:
struct A { int m1; double m2; }; void f(A); f( {'a', 'b'} ); // ok: f(A(int,double)) user-defined conversion f( {1.0} ); // error: narrowing--- end example]
Otherwise, if the parameter is a reference, see 13.3.3.1.4 [over.ics.ref]. [ Note: The rules in this section will apply for initializing the underlying temporary for the reference. --end note ] [ Example:
struct A { int m1; double m2; }; void f(const A&); f( {'a', 'b'} ); // ok: f(A(int,double)) user-defined conversion f( {1.0} ); // error: narrowing void g(const double &); g({1}); // same conversion as int to double--- end example]
Otherwise, if the parameter type is not a class:
if the initializer list has one element, the implicit conversion sequence is the one required to convert the element to the parameter type; [ Example:
void f(int); f( {'a'} ); // ok: same conversion as char to int f( {1.0} ); // error: narrowing--- end example]
if the initializer list has no elements, the implicit conversion sequence is the identity conversion. [ Example:
void f(int); f( { } ); // ok: identity conversion--- end example]
In all cases other than those enumerated above, no conversion is possible.
In 14.5.3 [temp.variadic], paragraph 4, change the first bullet:
In an expression-list (5.2); the pattern is an
assignment-expressioninitializer-clause.
In 14.8.2.1 [temp.deduct.call], paragraph 1:
Template argument deduction is done by comparing each function template parameter type (call it P) with the type of the corresponding argument of the call (call it A) as described below. If removing references and cv-qualifiers from P gives std::initializer_list<P'> for some P' and the argument is an initializer list ([dcl.init.list]), then deduction is performed instead for each element of the initializer list, taking P' as a function template parameter type and the initializer element as its argument. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context (14.8.2.5). [Example:
template<class T> void f(std::initializer_list<T>); // #1 f({1,2,3}); // T deduced to int f({1,"asdf"}); // error: T deduced to both int and const char* template<class T> void g(T); g({1,2,3}); // error: no argument deduced for T--- end example]
For a function parameter pack, ...
In 14.8.2.5 [temp.deduct.type] paragraph 5, add as a final bullet at the top level (not the second bullet level)
A function parameter for which the associated argument is an initializer list ([dcl.init.list]) but the parameter does not have std::initializer_list or reference to possibly cv-qualified std::initializer_list type. [Example:
template<class T> void g(T); g({1,2,3}); // error: no argument deduced for T--- end example]
In 18 [language.support] paragraph 2, change
The following subclauses describe common type definitions used throughout the library, characteristics of the predefined types, functions supporting start and termination of a C++ program, support for dynamic memory management, support for dynamic type identification, support for exception processing, support for initializer lists, and other runtime support, as summarized in Table 16.
...and add 18.7 Initializer lists <initializer_list> to Table 16.
Add a new section after 18.7 [support.exception] and before 18.8 [support.runtime]:
18.8. Initializer lists [support.initlist]
The header <initializer_list> defines one type.
template<class E> class initializer_list { public: initializer_list(); size_t size() const; // number of elements const E* begin() const; // first element const E* end() const; // one past the last element };An initializer_list provides access to an array of objects of type const E. [ Note: A pair of pointers or a pointer plus a length would be obvious representations for initializer_list; initializer_list is used to implement initializer lists as specified in [dcl.init.list]. Copying an initializer list does not copy the underlying elements. --- end note]
18.8.1 Initializer list constructors [support.initlist.cons]
initializer_list();Effects: constructs an empty initializer list
Postconditions: size() == 0
Throws: nothing18.8.2 Initializer list access [support.initlist.access]
const E* begin() const;Returns: a pointer to the beginning of the array; if size()==0 the value of begin() and end() are unspecified but identical; that is, begin()==end().
Throws: nothingconst E* end() const;Returns: begin() + size()
Throws: nothingsize_t size() const;Returns: the number of elements in the array
Throws: nothing
In 20.2 Utility components [utility] paragraph 1:
This subclause contains some basic function and class templates that are used throughout the rest of the library.
Header <utility> synopsis
#include<initializer_list> namespace std {