List-initialization (since C++11)

Initializes an object from a brace-enclosed initializer list.

# Notes

Every initializer clause is sequenced before any initializer clause that follows it in the brace-enclosed initializer list. This is in contrast with the arguments of a function call expression, which are unsequenced(until C++17)indeterminately sequenced(since C++17).

A brace-enclosed initializer 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 brace-enclosed initializer list, so given the declaration template void f(T); the expression f({1, 2, 3}) is ill-formed. However, the template parameter can otherwise be deduced, as is the case for std::vector v(std::istream_iterator(std::cin), {}), where the iterator type is deduced by the first argument but also used in the second parameter position. A special exception is made for type deduction using the keyword auto, which deduces any brace-enclosed initializer list as std::initializer_list in copy-list-initialization.

Also because a brace-enclosed initializer list has no type, special rules for overload resolution apply when it is used as an argument to an overloaded function call.

Aggregates copy/move initialize directly from brace-enclosed initializer list of a single initializer clause of the same type, but non-aggregates consider std::initializer_list constructors first:

Some compilers (e.g., gcc 10) only consider conversion from a pointer or a pointer-to-member to bool narrowing in C++20 mode.

# Example

#include <iostream>
#include <map>
#include <string>
#include <vector>
 
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 prvalue, then copy-init
    auto s4 = std::string{"HelloWorld"}; // same as above, no temporary
                                         // created since C++17
 
    std::map<int, std::string> m = // nested list-initialization
    {
        {1, "a"},
        {2, {'a', 'b', 'c'}},
        {3, s1}
    };
 
    std::cout << f({"hello", "world"}).first // list-initialization in function call
              << '\n';
 
    const int (&ar)[2] = {1, 2}; // binds an lvalue reference to a temporary array
    int&& r1 = {1}; // binds an 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 << ' ';
    std::cout << '\n';
 
    [](...){}(d, ar, r1, uc1); // has effect of [[maybe_unused]]
}

# Defect reports

DRApplied toBehavior as publishedCorrect behavior
CWG 1288C++11list-initializing a reference with a brace-enclosed initializer list of asingle initializer clause always bound the reference to a temporarybind to that initializerclause if valid
CWG 1290C++11the lifetime of the backing array was not correctly specifiedspecified same as othertemporary objects
CWG 1324C++11initialization considered first for initialization from {}aggregate initializationconsidered first
CWG 1418C++11the type of the backing array lacked constconst added
CWG 1467C++11same-type initialization of aggregates and characterarrays was prohibited; initializer-list constructors hadpriority over copy constructors for single-clause listssame-type initializationallowed; single-clauselists initialize directly
CWG 1494C++11when list-initializing a reference with an initializer clause of anincompatible type, it was unspecified whether the temporarycreated is direct-list-initialized or copy-list-initializedit depends on thekind of initializationfor the reference
CWG 2137C++11initializer-list constructors lost to copyconstructors when list-initializing X from {X}non-aggregates considerinitializer-lists first
CWG 2252C++17enumerations could be list-initialized from non-scalar valuesprohibited
CWG 2267C++11the resolution of CWG issue 1494 made clearthat temporaries could be direct-list-initializedthey are copy-list-initializedwhen list-initializing references
CWG 2374C++17direct-list-initialization of an enum allowed too many source typesrestricted
CWG 2627C++11a narrow bit-field of a larger integer type can be promoted toa smaller integer type, but it was still a narrowing conversionit is not anarrowing conversion
CWG 2713C++20references to aggregate classes could notbe initialized by designated initializer listsallowed
CWG 2830C++11list-initialization did not ignore the top-level cv-qualificationignores
CWG 2864C++11floating-point conversions that overflow were not narrowingthey are narrowing
P1957R2C++11conversion from a pointer/pointer-to-memberto bool was not narrowingconsidered narrowing
P2752R3C++11backing arrays with overlapping lifetime could not overlapthey may overlap

# See also