Namespaces
Variants

Aggregate initialization

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
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
Special member functions
Templates
Miscellaneous

Initialisiert ein Aggregat aus einer Initialisierungsliste . Es ist eine Form der list-initialization (since C++11) .

Inhaltsverzeichnis

Syntax

T Objekt = { arg1, arg2, ... }; (1)
T Objekt { arg1, arg2, ... }; (2) (seit C++11)
T Objekt = { . des1 = arg1 , . des2 { arg2 } ... }; (3) (seit C++20)
T Objekt { . des1 = arg1 , . des2 { arg2 } ... }; (4) (seit C++20)
1,2) Initialisierung eines Aggregats mit einer gewöhnlichen Initialisierungsliste.
3,4) Initialisierung eines Aggregats mit designated initializers (nur für Aggregate-Klassen).

Definitionen

Aggregat

Ein aggregate ist einer der folgenden Typen:

  • Array-Typen
  • Klassentypen, die
  • keine benutzerdeklarierten Konstruktoren
(bis C++11)
(seit C++11)
(bis C++20)
  • keine benutzerdeklarierten oder geerbten Konstruktoren
(seit C++20)
  • keine privaten oder geschützten direkten nicht-statischen Datenelemente
(bis C++17)
(seit C++17)
  • keine virtuellen Memberfunktionen
(seit C++11)
(bis C++14)

Element

Die Elemente eines Aggregats sind:

  • für ein Array, die Array-Elemente in aufsteigender Indexreihenfolge, oder
  • für eine Klasse, die nicht-statischen Datenelemente, die keine anonymen Bit-Felder sind, in Deklarationsreihenfolge.
(bis C++17)
  • für eine Klasse, die direkten Basisklassen in Deklarationsreihenfolge, gefolgt von den direkten nicht-statischen Datenelementen, die weder anonyme Bit-Felder noch Elemente einer anonymen Union sind, in Deklarationsreihenfolge.
(seit C++17)

Zugehörigkeit

Jede Initialisierer-Klausel in einer geschweiften Klammer-Initialisiererliste wird als zugehörig zu einem Element des zu initialisierenden Aggregats oder zu einem Element eines seiner Subaggregate bezeichnet.

Unter Berücksichtigung der Abfolge der Initialisierungsausdrücke und der Abfolge der Aggregat-Elemente, die ursprünglich als Abfolge der Elemente des zu initialisierenden Aggregats gebildet werden und potenziell wie folgt modifiziert werden:

  • Für jeden Initialisierer-Ausdruck, falls eine der folgenden Bedingungen erfüllt ist, bezieht er sich auf das entsprechende Aggregat-Element elem :
  • elem ist kein Aggregat.
  • Die Initialisiererklausel beginnt mit { .
  • Die Initialisiererklausel ist ein Ausdruck, und eine implizite Konvertierungssequenz kann gebildet werden, die den Ausdruck in den Typ von elem konvertiert.
  • elem ist ein Aggregat, das selbst keine Aggregatelemente besitzt.
  • Andernfalls ist elem ein Aggregat und dieses Subaggregat wird in der Liste der Aggregationselemente durch die Sequenz seiner eigenen Aggregationselemente ersetzt, und die Zugehörigkeitsanalyse wird mit dem ersten solchen Element und derselben Initialisiererklausel fortgesetzt. Mit anderen Worten, diese Regeln werden rekursiv auf die Subaggregate des Aggregats angewendet.

Die Analyse ist abgeschlossen, wenn alle Initialisierer-Klauseln erschöpft sind. Wenn eine Initialisierer-Klausel verbleibt, die sich nicht auf ein Element des Aggregats oder eines seiner Subaggregate bezieht, ist das Programm fehlerhaft.

struct S1 { long a, b; };
struct S2 { S1 s, t; };
// Jeder Teilaggregat von „x“ wird einem Initialisierer-Klausel zugeordnet, die mit { beginnt
S2 x[2] =
{
    // gehört zu „x[0]“
    {
        {1L, 2L}, // gehört zu „x[0].s“
        {3L, 4L}  // gehört zu „x[0].t“
    },
    // gehört zu „x[1]“
    {
        {5L, 6L}, // gehört zu „x[1].s“
        {7L, 8L}  // gehört zu „x[1].t“
    }
};
// „x“ und „y“ haben denselben Wert (siehe unten)
S2 y[2] = {1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L};
// Der Prozess der Zuordnungsanalyse von „y“:
// 1. Initialisiert die Aggregat-Element-Sequenz (x[0], x[1]) und
//    die Initialisierer-Klausel-Sequenz (1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L).
// 2. Beginnend mit den ersten Elementen jeder Sequenz,
//    prüft, ob 1L zu x[0] gehört:
//    · x[0] ist ein Aggregat.
//    · 1L beginnt nicht mit {.
//    · 1L ist ein Ausdruck, kann aber nicht implizit zu S2 konvertiert werden.
//    · x[0] hat Aggregat-Elemente.
// 3. 1L kann nicht zu x[0] gehören, daher wird x[0] durch x[0].s und x[0].t ersetzt,
//    die Aggregat-Element-Sequenz wird zu (x[0].s, x[0].t, x[1]).
// 4. Setzt die Zuordnungsprüfung fort, aber 1L kann auch nicht zu x[0].s gehören.
// 5. Die Aggregat-Element-Sequenz wird nun zu (x[0].s.a, x[0].s.b, x[0].t, x[1]).
// 6. Setzt die Zuordnungsprüfung erneut fort:
//    1L gehört zu x[0].s.a und 2L gehört zu x[0].s.b.
// 7. Der Rest der Zuordnungsanalyse funktioniert ähnlich.
char cv[4] = {'a', 's', 'd', 'f', 0}; // Fehler: zu viele Initialisierer-Klauseln

Initialisierungsprozess

Bestimmung der Elementart

Die Auswirkungen der Aggregatinitialisierung sind:

1) Bestimmen Sie die explizit initialisierten Elemente des Aggregats wie folgt:
  • Wenn die Initialisierungsliste eine designated initializer list ist (das Aggregat kann nur vom Klassentyp sein), muss der Bezeichner in jedem Designator ein direktes nicht-statisches Datenelement der Klasse benennen, und die explizit initialisierten Elemente des Aggregats sind die Elemente, die diese Member sind oder enthalten.
(seit C++20)
  • Andernfalls, (seit C++20) wenn die Initialisierungsliste nicht leer ist, sind die explizit initialisierten Elemente des Aggregats die Elemente mit einer zugehörigen Initialisierungsanweisung und die Elemente, die ein Subaggregat mit einer zugehörigen Initialisierungsanweisung haben.
  • Andernfalls muss die Initialisierungsliste leer sein ( { } ), und es gibt keine explizit initialisierten Elemente.
Das Programm ist fehlerhaft, wenn das Aggregat eine Union ist und zwei oder mehr explizit initialisierte Elemente vorhanden sind:
union u { int a; const char* b; };
u a = {1};                   // OK: initialisiert explizit Member `a`
u b = {0, "asdf"};           // Fehler: initialisiert explizit zwei Member
u c = {"asdf"};              // Fehler: int kann nicht mit "asdf" initialisiert werden
// C++20 Designated Initializer Lists
u d = {.b = "asdf"};         // OK: kann einen nicht-initialen Member explizit initialisieren
u e = {.a = 1, .b = "asdf"}; // Fehler: initialisiert explizit zwei Member
2) Initialisiere jedes Element des Aggregats in der Elementreihenfolge. Das heißt, alle Werteberechnungen und Nebeneffekte eines gegebenen Elements sind sequenziert vor denen jedes Elements, das ihm in der Reihenfolge folgt (since C++11) .

Explizit initialisierte Elemente

Für jedes explizit initialisierte Element:

  • Wenn das Element ein anonymes Union-Mitglied ist und die Initialisierungsliste eine designated initializer list ist, wird das Element durch die designated initializer list { D } initialisiert, wobei D die designated initializer clause ist, die ein Mitglied des anonymen Union-Mitglieds benennt. Es darf nur eine solche designated initializer clause geben.
struct C
{
    union
    {
        int a;
        const char* p;
    };
    int x;
} c = {.a = 1, .x = 3}; // initializes c.a with 1 and c.x with 3
  • Andernfalls, wenn die Initialisierungsliste eine designated initializer list ist, wird das Element mit dem Initialisierer der entsprechenden designated initializer clause initialisiert.
  • Wenn dieser Initialisierer von Syntax (1) ist und eine einschränkende Konvertierung erforderlich ist, um den Ausdruck zu konvertieren, ist das Programm fehlerhaft.
(seit C++20)


  • Die Initialisierungsliste ist eine in geschweifte Klammern eingeschlossene Initialisierungsliste:
(until C++20)
  • Andernfalls ist die Initialisierungsliste eine nicht-designierte in geschweifte Klammern eingeschlossene Initialisierungsliste:
(since C++20)
  • Wenn ein Initialisierer-Ausdruck zum Aggregat-Element gehört, dann wird das Aggregat-Element copy-initialized vom Initialisierer-Ausdruck.
  • Andernfalls wird das Aggregat-Element aus einer geschweiften Initialisiererliste kopierinitialisiert, die alle Initialisierer-Ausdrücke enthält, die zu Unterobjekten des Aggregat-Elements gehören, in der Reihenfolge ihres Auftretens.
struct A
{
    int x;
    struct B
    {
        int i;
        int j;
    } b;
} a = {1, {2, 3}}; // initialisiert a.x mit 1, a.b.i mit 2, a.b.j mit 3
struct base1 { int b1, b2 = 42; };
struct base2
{
    base2()
    {
        b3 = 42;
    }
    int b3;
};
struct derived : base1, base2
{
    int d;
};
derived d1{{1, 2}, {}, 4}; // initialisiert d1.b1 mit 1, d1.b2 mit 2,
                           //             d1.b3 mit 42, d1.d mit 4
derived d2{{}, {}, 4};     // initialisiert d2.b1 mit 0, d2.b2 mit 42,
                           //             d2.b3 mit 42, d2.d mit 4

Implizit initialisierte Elemente

Für ein nicht-Union-Aggregat wird jedes Element, das kein explizit initialisiertes Element ist, wie folgt initialisiert:

(since C++11)
  • Andernfalls, wenn das Element keine Referenz ist, wird das Element copy-initialized aus einer leeren Initialisierungsliste.
  • Andernfalls ist das Programm fehlerhaft.
struct S
{
    int a;
    const char* b;
    int c;
    int d = b[a];
};
// initialisiert ss.a mit 1,
//             ss.b mit "asdf",
//             ss.c mit dem Wert eines Ausdrucks der Form int{} (d.h. 0),
//         und ss.d mit dem Wert von ss.b[ss.a] (d.h. 's')
S ss = {1, "asdf"};

Wenn das Aggregat eine Union ist und die Initialisierungsliste leer ist, dann

  • Wenn ein beliebiges Variant-Mitglied einen Standard-Mitgliedsinitialisierer hat, wird dieses Mitglied aus seinem Standard-Mitgliedsinitialisierer initialisiert.
(since C++11)
  • Andernfalls wird das erste Mitglied der Union (falls vorhanden) aus einer leeren Initialisierungsliste kopierinitialisiert.

Arrays mit unbekannten Grenzen

Die Anzahl der Elemente in einem Array unbekannter Größe, das mit einer geschweiften Klammer-Initialisierungsliste initialisiert wird, ist die Anzahl der explizit initialisierten Elemente des Arrays. Ein Array unbekannter Größe kann nicht mit { } initialisiert werden.

int x[] = {1, 3, 5}; // x hat 3 Elemente
struct Y { int i, j, k; };
Y y[] = {1, 2, 3, 4, 5, 6}; // y hat nur 2 Elemente:
                            // 1, 2 und 3 gehören zu y[0],
                            // 4, 5 und 6 gehören zu y[1]
int z[] = {} // Fehler: Kann kein Array ohne Elemente deklarieren

Designated initializers

Die Syntaxformen (3,4) werden als designated initializers bezeichnet: Jeder designator muss ein direktes nicht-statisches Datenelement von T benennen, und alle designator s im Ausdruck müssen in derselben Reihenfolge wie die Datenelemente von T erscheinen.

struct A { int x; int y; int z; };
A a{.x = 1, .y = 2, .z = 3}; // ok
A b{.y = 2, .z = 3, .x = 1}; // error; designator order does not match declaration order

Jedes direkte nicht-statische Datenelement, das durch den designated initializer benannt wird, wird aus dem entsprechenden geschweiften Klammer- oder Gleichheitsinitialisierer initialisiert, der auf den designator folgt. Einschränkende Konvertierungen sind verboten.

Designated initializer können verwendet werden, um eine union in einen anderen Zustand als den ersten zu initialisieren. Für eine union darf nur ein Initialisierer angegeben werden.

union u { int a; const char* b; };
u f = {.b = "asdf"};         // OK, active member of the union is b
u g = {.a = 1, .b = "asdf"}; // Error, only one initializer may be provided

Für ein Nicht-Union-Aggregat werden Elemente, für die kein designated initializer angegeben ist, genauso initialisiert wie oben beschrieben, wenn die Anzahl der Initialisiererklauseln kleiner ist als die Anzahl der Elemente (Standard-Member-Initialisierer, falls vorhanden, andernfalls leere Listeninitialisierung):

struct A
{
    string str;
    int n = 42;
    int m = -1;
};
A{.m = 21} // Initializes str with {}, which calls the default constructor
           // then initializes n with = 42
           // then initializes m with = 21
struct A { int x; int y; int z; };
A a{.x = 1, .z = 2}; // ok, b.y initialized to 0
A b{.y = 2, .x = 1}; // error; designator order does not match declaration order
A c{.y = 2}; // ok, c.x and c.z are initialized to 0
constexpr A d{.z = 2}; // can be used with constexpr, as opposed to: constexpr A d;
static_assert(d.x == 0 && d.y == 0); // d.x and d.y are initialized to 0

Wenn das Aggregat, das mit einer designated initializer-Klausel initialisiert wird, ein anonymes union-Mitglied hat, muss der entsprechende designated initializer eines der Mitglieder dieser anonymen union benennen.

Hinweis: Out-of-order designated initialization, verschachtelte designated initialization, das Vermischen von designated initializers und regulären Initialisierern sowie die designated initialization von Arrays werden in der C-Programmiersprache unterstützt, sind jedoch in C++ nicht erlaubt.

struct A { int x, y; };
struct B { struct A a; };
struct A a = {.y = 1, .x = 2}; // valid C, invalid C++ (out of order)
int arr[3] = {[1] = 5};        // valid C, invalid C++ (array)
struct B b = {.a.x = 0};       // valid C, invalid C++ (nested)
struct A a = {.x = 1, 2};      // valid C, invalid C++ (mixed)
(seit C++20)

Zeichenfelder

Arrays von gewöhnlichen Zeichentypen ( char , signed char , unsigned char ) , char8_t (seit C++20) , char16_t , char32_t (seit C++11) oder wchar_t können mit gewöhnlichen String-Literalen , UTF-8-String-Literalen (seit C++20) , UTF-16-String-Literalen, UTF-32-String-Literalen (seit C++11) bzw. breiten String-Literalen initialisiert werden, optional in geschweiften Klammern eingeschlossen . Zusätzlich kann ein Array von char oder unsigned char durch ein UTF-8-String-Literal initialisiert werden, optional in geschweiften Klammern eingeschlossen (seit C++20) . Aufeinanderfolgende Zeichen des String-Literals (einschließlich des impliziten abschließenden Nullzeichens) initialisieren die Elemente des Arrays , mit einer integralen Konvertierung falls notwendig für Quell- und Zielwert (seit C++20) . Wenn die Größe des Arrays angegeben ist und größer als die Anzahl der Zeichen im String-Literal ist, werden die verbleibenden Zeichen mit Nullen initialisiert.

char a[] = "abc";
// entspricht char a[4] = {'a', 'b', 'c', '\0'};
//  unsigned char b[3] = "abc"; // Fehler: Initialisierungsstring zu lang
unsigned char b[5]{"abc"};
// entspricht unsigned char b[5] = {'a', 'b', 'c', '\0', '\0'};
wchar_t c[] = {L"кошка"}; // optionale Klammern
// entspricht wchar_t c[6] = {L'к', L'о', L'ш', L'к', L'а', L'\0'};

Hinweise

Eine Aggregatklasse oder ein Array kann nicht-Aggregat- public bases (since C++17) , Member oder Elemente enthalten, die wie oben beschrieben initialisiert werden (z.B. Copy-Initialisierung vom entsprechenden Initialisierer).

Bis C++11 waren verengende Konvertierungen in der Aggregatinitialisierung erlaubt, sind jedoch nicht mehr zulässig.

Bis C++11 konnte die Aggregatinitialisierung nur in Variablendefinitionen verwendet werden und konnte aufgrund von Syntaxeinschränkungen nicht in einer Konstruktor-Initialisierungsliste , einem new-Ausdruck oder bei der Erstellung temporärer Objekte eingesetzt werden.

In C kann ein Zeichenarray mit einer Größe von eins weniger als die Größe des String-Literals von einem String-Literal initialisiert werden; das resultierende Array ist nicht nullterminiert. Dies ist in C++ nicht erlaubt.

Feature-Test-Makro Wert Std Feature
__cpp_aggregate_bases 201603L (C++17) Aggregatklassen mit Basisklassen
__cpp_aggregate_nsdmi 201304L (C++14) Aggregatklassen mit Standard-Member-Initialisierern
__cpp_aggregate_paren_init 201902L (C++20) Aggregatinitialisierung in Form von direkter Initialisierung
__cpp_char8_t 202207L (C++23)
(DR20)
char8_t Kompatibilitäts- und Portabilitätskorrektur ( Ermöglicht Initialisierung von ( unsigned char Arrays aus UTF-8-Zeichenkettenliteralen )
__cpp_designated_initializers 201707L (C++20) Designated Initializers

Beispiel

#include <array>
#include <cstdio>
#include <string>
struct S
{
    int x;
    struct Foo
    {
        int i;
        int j;
        int a[3];
    } b;
};
int main()
{
    S s1 = {1, {2, 3, {4, 5, 6}}};
    S s2 = {1, 2, 3, 4, 5, 6}; // gleich, aber mit Klammerauslassung
    S s3{1, {2, 3, {4, 5, 6}}}; // gleich, mit Direct-List-Initialisierungs-Syntax
    S s4{1, 2, 3, 4, 5, 6}; // Fehler bis CWG 1270:
                            // Klammerauslassung nur mit Gleichheitszeichen erlaubt
    int ar[] = {1, 2, 3}; // ar ist int[3]
//  char cr[3] = {'a', 'b', 'c', 'd'}; // zu viele Initialisierungsausdrücke
    char cr[3] = {'a'}; // Array initialisiert als {'a', '\0', '\0'}
    int ar2d1[2][2] = {{1, 2}, {3, 4}}; // vollständig geklammerte 2D-Array: {1, 2}
                                        //                        {3, 4}
    int ar2d2[2][2] = {1, 2, 3, 4}; // Klammerauslassung: {1, 2}
                                    //                {3, 4}
    int ar2d3[2][2] = {{1}, {2}};   // nur erste Spalte: {1, 0}
                                    //                    {2, 0}
    std::array<int, 3> std_ar2{{1, 2, 3}};  // std::array ist ein Aggregat
    std::array<int, 3> std_ar1 = {1, 2, 3}; // Klammerauslassung okay
//  int ai[] = {1, 2.0}; // einschränkende Konvertierung von double zu int:
                         // Fehler in C++11, okay in C++03
    std::string ars[] = {std::string("one"), // Copy-Initialisierung
                         "two",              // Konvertierung, dann Copy-Initialisierung
                         {'t', 'h', 'r', 'e', 'e'}}; // Listeninitialisierung
    union U
    {
        int a;
        const char* b;
    };
    U u1 = {1};         // OK, erstes Mitglied der Union
//  U u2 = {0, "asdf"}; // Fehler: zu viele Initialisierer für Union
//  U u3 = {"asdf"};    // Fehler: ungültige Konvertierung zu int
    [](...) { std::puts("Garbage collecting unused variables... Done."); }
    (
        s1, s2, s3, s4, ar, cr, ar2d1, ar2d2, ar2d3, std_ar2, std_ar1, u1
    );
}
// Aggregat
struct base1 { int b1, b2 = 42; };
// Nicht-Aggregat
struct base2
{
    base2() : b3(42) {}
    int b3;
};
// Aggregat 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

Ausgabe:

Garbage collecting unused variables... Done.

Fehlerberichte

Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.

DR Angewendet auf Verhalten wie veröffentlicht Korrigiertes Verhalten
CWG 413 C++98 anonyme Bitfelder wurden in der Aggregatinitialisierung initialisiert sie werden ignoriert
CWG 737 C++98 wenn ein Zeichenarray mit einem String-Literal initialisiert wird,
das weniger Zeichen als die Arraygröße hat, waren die Zeichen-
elemente nach dem abschließenden ' \0 ' nicht initialisiert
sie werden
Null-initialisiert
CWG 1270 C++11 Klammer-Elision war nur in der Copy-List-Initialisierung erlaubt auch anderswo erlaubt
CWG 1518 C++11 eine Klasse, die einen expliziten Standardkonstruktor deklariert oder
geerbte Konstruktoren hat, könnte ein Aggregat sein
sie ist kein
Aggregat
CWG 1622 C++98 eine Union konnte nicht mit { } initialisiert werden erlaubt
CWG 2149
( P3106R1 )
C++98 es war unklar, ob Klammer-Elision während
der Arraygrößenableitung anwendbar ist
anwendbar
CWG 2272 C++98 ein nicht-statisches Referenzmitglied, das nicht explizit
initialisiert wurde, wurde aus einer leeren Initialisierungsliste kopier-initialisiert
das Programm ist in diesem
Fall fehlerhaft
CWG 2610 C++17 Aggregattypen konnten keine privaten oder geschützten indirekten Basisklassen haben erlaubt
CWG 2619 C++20 die Art der Initialisierung durch designierte Initialisierer war unklar sie hängt von der
Art des Initialisierers ab
P2513R4 C++20 ein UTF-8-String-Literal konnte kein Array von char
oder unsigned char initialisieren, was inkompatibel mit C oder C++17 war
solche Initialisierung
ist gültig

Siehe auch

C-Dokumentation für Struct and union initialization