aggregate initialization

< cpp‎ | language

Initializes an aggregate from braced-init-list


[edit] Syntax

T object = {arg1, arg2, ...}; (1)
T object {arg1, arg2, ...}; (2) (since C++11)

[edit] Explanation

Aggregate initialization is a form of list-initialization, which initializes aggregates

An aggregate is one of the following types:

  • array type
  • class type (typically, struct or union), that has
  • no private or protected non-static data members
  • no user-provided constructors , including those inherited from public bases (since C++17) (explicitly defaulted or deleted constructors are allowed) (since C++11)
  • no virtual, private, or protected (since C++17) base classes
  • no virtual member functions
(since C++11)
(until C++14)

The effects of aggregate initialization are:

  • Each direct public base, (since C++17) array element, or non-static class member, in order of array subscript/appearance in the class definition, is copy-initialized from the corresponding clause of the initializer list.
  • If the initializer clause is an expression, implicit conversions are allowed as per copy-initialization, except if they are narrowing (as in list-initialization) (since C++11).
  • If the initializer clause is a nested braced-init-list (which is not an expression), the corresponding class member or public base (since C++17) is list-initialized from that clause: aggregate initialization is recursive.
  • If the object is an array of unknown size, and the supplied brace-enclosed initializer list has n clauses, the size of the array is n
  • Static data members and anonymous bit-fields are skipped during aggregate initialization.
  • If the number of initializer clauses exceeds the number of members and bases (since C++17) to initialize, the program is ill-formed (compiler error)
  • If the number of initializer clauses is less than the number of members and bases (since C++17) or initializer list is completely empty, the remaining members and bases (since C++17) are initialized by their default initializers, if provided in the class definition, and otherwise (since C++14) by empty lists, which performs value-initialization. If a member of a reference type is one of these remaining members, the program is ill-formed (references cannot be value-initialized)
  • If the aggregate initialization uses the form with the equal sign (T a = {args..}), (until C++14) the braces around the nested initializer lists may be elided (omitted), in which case as many initializer clauses as necessary are used to initialize every member or element of the corresponding subaggregate, and the subsequent initializer clauses are used to initialize the following members of the object. However, if the object has a sub-aggregate without any members (an empty struct, or a struct holding only static members), brace elision is not allowed, and an empty nested list {} must be used.
  • When a union is initialized by aggregate initialization, only its first non-static data member is initialized.

[edit] Character arrays

Arrays of character types (char, signed char, unsigned char, char16_t, char32_t, wchar_t) can be initialized from an appropriate string literal, optionally enclosed in braces. Successive characters of the string literal (which includes the implicit terminating null character) initialize the elements of the array. If the size of the array is specified and it is larger than the number of characters in the string literal, the remaining characters are zero-initialized.

char a[] = "abc";
// equivalent to char a[4] = {'a', 'b', 'c', '\0'};
//  unsigned char b[3] = "abc"; // Error: initializer string too long
unsigned char b[5]{"abc"};
// equivalent to unsigned char b[5] = {'a', 'b', 'c', '\0', '\0'};
wchar_t c[] = {L"кошка"}; // optional braces
// equivalent to wchar_t c[6] = {L'к', L'о', L'ш', L'к', L'а', L'\0'};

[edit] Notes

An aggregate class or array may include non-aggregate public bases (since C++17), members, or elements, which are initialized as described above (e.g. copy-initialization from the corresponding initializer clause)

Until C++11, narrowing conversions were permitted in aggregate initialization, but they are no longer allowed.

Until C++11, aggregate initialization could not be used in a constructor initializer list due to syntax restrictions.

Until C++14, the direct-initialization form T a {args..} did not permit brace elision.

In C, character array of size one less than the size of the string literal may be initialized from a string literal; the resulting array is not null-terminated. This is not allowed in C++.

[edit] Example

#include <string>
#include <array>
struct S {
    int x;
    struct Foo {
        int i;
        int j;
        int a[3];
    } b;
union U {
    int a;
    const char* b;
int main()
    S s1 = { 1, { 2, 3, {4, 5, 6} } };
    S s2 = { 1, 2, 3, 4, 5, 6}; // same, but with brace elision
    S s3{1, {2, 3, {4, 5, 6} } }; // same, using direct-list-initialization syntax
    S s4{1, 2, 3, 4, 5, 6}; // error in C++11: brace-elision only allowed with equals sign
                            // okay in C++14
    int ar[] = {1,2,3}; // ar is int[3]
//  char cr[3] = {'a', 'b', 'c', 'd'}; // too many initializer clauses
    char cr[3] = {'a'}; // array initialized as {'a', '\0', '\0'}
    int ar2d1[2][2] = {{1, 2}, {3, 4}}; // fully-braced 2D array: {1, 2}
                                        //                        {3, 4}
    int ar2d2[2][2] = {1, 2, 3, 4}; // brace elision: {1, 2}
                                    //                {3, 4}
    int ar2d3[2][2] = {{1}, {2}};   // only first column: {1, 0}
                                    //                    {2, 0}
    std::array<int, 3> std_ar2{ {1,2,3} };    // std::array is an aggregate
    std::array<int, 3> std_ar1 = {1, 2, 3}; // brace-elision okay
    int ai[] = { 1, 2.0 }; // narrowing conversion from double to int:
                           // error in C++11, okay in C++03
    std::string ars[] = {std::string("one"), // copy-initialization
                         "two",              // conversion, then copy-initialization
                         {'t', 'h', 'r', 'e', 'e'} }; // list-initialization
    U u1 = {1}; // OK, first member of the union
//    U u2 = { 0, "asdf" }; // error: too many initializers for union
//    U u3 = { "asdf" }; // error: invalid conversion to int
// aggregate
struct base1 { int b1, b2 = 42; };
// non-aggregate
struct base2 {
  base2() : b3(42) {}
  int b3;
// aggregate in C++17
struct derived : base1, base2 { int d; };
derived d1{ {1, 2}, { }, 4}; // d1.b1 = 1, d1.b2 = 2,  d1.b3 = 42, d1.d = 4
derived d2{ {    }, { }, 4}; // d2.b1 = 0, d2.b2 = 42, d2.b3 = 42, d2.d = 4

[edit] See also