Namespaces
Variants
Views
Actions

Difference between revisions of "cpp/language/list initialization"

From cppreference.com
< cpp‎ | language
(Remove the syntaxes that are *not* list-initialization, but involve overload resolution with a braced-init-list used as a parameter. Remove the non-static data member initializer case as redundant.)
(Clearer description of how auto interacts with braced-init-lists.)
Line 64: Line 64:
 
A braced-init-list is not an expression and therefore has no type, e.g. {{c|decltype({1,2})}} is ill-formed. Having no type implies that template type deduction cannot deduce a type that matches a braced-init-list, so given the declaration {{c|template<class T> void f(T);}} the expression {{lc|f({1,2,3})}} is ill-formed. It is possible to deduce a {{c|std::initializer_list<T>}} as a template function argument, however: {{c|template<class T> void f(std::initializer_list<T>); f({1,2,3});}} is well-formed and equivalent to {{c|f<int>(std::initializer_list<int>{1,2,3})}}.
 
A braced-init-list is not an expression and therefore has no type, e.g. {{c|decltype({1,2})}} is ill-formed. Having no type implies that template type deduction cannot deduce a type that matches a braced-init-list, so given the declaration {{c|template<class T> void f(T);}} the expression {{lc|f({1,2,3})}} is ill-formed. It is possible to deduce a {{c|std::initializer_list<T>}} as a template function argument, however: {{c|template<class T> void f(std::initializer_list<T>); f({1,2,3});}} is well-formed and equivalent to {{c|f<int>(std::initializer_list<int>{1,2,3})}}.
  
A special exception is made for the keyword {{c|auto}}, which deduces ''any'' braced-init-list as {{lc|std::initializer_list}}. This can be confusing to the programmer who naively expects that given {{c|auto i = {13};}} the variable {{lc|i}} will have type {{lc|int}} and value 13 only to discover that in fact {{lc|i}} is a {{lc|std::initializer_list<int>}} with a single element whose value is 13.
+
A special exception is made for type deduction using the keyword {{c|auto}}, which deduces ''any'' braced-init-list as {{lc|std::initializer_list}}. This can be confusing to the programmer who naively expects that given {{c|auto i = {13};}} the variable {{lc|i}} will have type {{lc|int}} and value 13 only to discover that in fact {{lc|i}} is a {{lc|std::initializer_list<int>}} with a single element whose value is 13.
  
 
===Example===
 
===Example===

Revision as of 15:05, 6 November 2014

 
 
C++ language
General topics
Flow control
Conditional execution statements
if
Iteration statements (loops)
for
range-for (C++11)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications (until C++17*)
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
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
explicit (C++11)
static

Special member functions
Templates
Miscellaneous
 
 

Initializes an object from braced-init-list

Contents

Syntax

T object { arg1, arg2, ... }; (1)
T { arg1, arg2, ... }; (2)
new T { arg1, arg2, ... }; (3)
return { arg1, arg2, ... } ; (4)
Class::Class() : member{arg1, arg2, ...} {... (5)
T object = {arg1, arg2, ...}; (6)

Explanation

List initialization is performed in the following situations:

1) initialization of a named variable with a brace-enclosed list of expressions or nested lists (braced-init-list)
2) initialization of an unnamed temporary with a braced-init-list
3) initialization of an object with dynamic storage duration with a new-expression, where the initializer is a brace-init-list
4) in a return statement with braced-init-list used as the return expression
5) in a constructor initializer list
6) on the right-hand-side of the equals sign (similar to copy-initialization)

The effects of list initialization of an object of type T are:

  • If the braced-init-list is empty and T is a class type with a default constructor, value-initialization is performed.
(until C++14)
  • Otherwise, If the braced-init-list is empty and T is a class type with a default constructor, value-initialization is performed.
(since C++14)
  • Otherwise, if T is a specialization of std::initializer_list, a new std::initializer_list prvalue of the same type is constructed and used to direct-initialize or copy-initialize the object of type T, depending on context.
  • Otherwise, the constructors of T are considered, in two phases:
  • All constructors that take std::initializer_list as the only argument, or as the first argument if the remaining arguments have default values, are examined, and matched by overload resolution against a single argument of type std::initializer_list
  • If the previous stage does not produce a match, all constructors of T participate in overload resolution against the set of arguments that consists of the elements of the braced-init-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)
  • Otherwise, if the braced-init-list has only one element and either T isn't a reference type or is reference type that isn't compatible with the type of the element, T is direct-initialized or copy-initialized, depending on context, except that narrowing conversions are not allowed.
  • Otherwise, if T is reference type, a prvalue temporary of the referenced type is list-initialized, and the reference is bound to that temporary. (this fails if the reference is a non-const lvalue reference)

Narrowing conversions

list-initialization limits the allowed implicit conversions by prohibiting the following:

  • conversion from a floating-point type to an integer type
  • conversion from a long double to double or to float and conversion from double to float, except where the source is a constant expression whose value can be stored exactly in the target type
  • conversion from integer or unscoped enumeration type to integer type that cannot represent all values of the original, except where source is a constant expression whose value can be stored exactly in the target type

Notes

A braced-init-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 braced-init-list, so given the declaration template<class T> void f(T); the expression f({1,2,3}) is ill-formed. It is possible to deduce a std::initializer_list<T> as a template function argument, however: template<class T> void f(std::initializer_list<T>); f({1,2,3}); is well-formed and equivalent to f<int>(std::initializer_list<int>{1,2,3}).

A special exception is made for type deduction using the keyword auto, which deduces any braced-init-list as std::initializer_list. This can be confusing to the programmer who naively expects that given {{{1}}} the variable i will have type int and value 13 only to discover that in fact i is a std::initializer_list<int> with a single element whose value is 13.

Example

#include <iostream>
#include <vector>
#include <map>
#include <string>
 
struct Foo {
    std::vector<int> mem = {1,2,3}; // list-initialization of a non-static member
    std::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 call
    std::string s2{s1, 2, 2};           // regular constructor call
    std::string s3{0x61, 'a'}; // initializer-list ctor is preferred to (int, char)
 
    int n2 = {1}; // copy-list-initialization
    double d = double{1.2}; // list-initialization of a temporary, then copy-init
 
    std::map<int, std::string> m = { // nested list-initialization
           {1, "a"},
           {2, {'a', 'b', 'c'} },
           {3, s1}
    };
 
    const int (&ar)[2] = {1,2}; // binds a lvalue reference to a temporary array
    int&& r1 = {1}; // binds a 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 conversion
    unsigned char 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 << ' ';
}

Output:

world
0 1 1
abcd cd aa
1 a
2 abc
3 abcd
1 2 3 -1 -2 -3


See also