Namensräume
Varianten
Aktionen

Aggregatinitialisierung

Von cppreference.com
< cpp‎ | Sprache
 
 
C++ Sprache
Allgemeine Themen
Kontrollfluss
Bedingte Ausführungsaussagen
if
Iterationsanweisungen (Schleifen)
for
Bereichs-for (C++11)
Sprunganweisungen
Funktionen
Funktionsdeklaration
Lambda-Funktionsausdruck
inline-Spezifizierer
Dynamische Ausnahmespezifikationen (bis C++17*)
noexcept-Spezifizierer (C++11)
Ausnahmen
Namensräume
Typen
Spezifizierer
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Speicherdauer-Spezifizierer
Initialisierung
Ausdrücke
Alternative Darstellungen
Literale
Boolesch - Ganzzahl - Gleitkommazahl
Zeichen - String - nullptr (C++11)
Benutzerdefinierte (C++11)
Dienstprogramme
Attribute (C++11)
Typen
typedef-Deklaration
Typalias-Deklaration (C++11)
Umwandlungen
Speicherzuweisung
Klassen
Klassenspezifische Funktionseigenschaften
explicit (C++11)
static

Spezielle Member-Funktionen
Templates
Sonstiges
 
 

Initialisiert ein Aggregat aus einer Initialisierungsliste. Es ist eine Form der Listeninitialisierung(seit C++11).

Inhalt

[bearbeiten] 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 normalen Initialisierungsliste.
3,4) Initialisierung eines Aggregats mit Bezeichnerinitialisierern (nur für Klassen-Aggregate).

[bearbeiten] Definitionen

[bearbeiten] Aggregat

Ein Aggregat ist einer der folgenden Typen

  • Arraytypen
  • Klassentypen, die
  • keine vom Benutzer deklarierten Konstruktoren haben
(bis C++11)
(seit C++11)
(bis C++20)
  • keine vom Benutzer deklarierten oder vererbten Konstruktoren haben
(seit C++20)
  • keine privaten oder geschützten direkten nicht-statischen Datenmember haben
(bis C++17)
(seit C++17)
  • keine virtuellen Memberfunktionen haben
(seit C++11)
(bis C++14)

[bearbeiten] Element

Die Elemente eines Aggregats sind

  • bei einem Array die Array-Elemente in aufsteigender Indexreihenfolge, oder
  • bei einer Klasse die nicht-statischen Datenmember, die keine anonymen Bitfelder sind, in Deklarationsreihenfolge.
(bis C++17)
  • bei einer Klasse die direkten Basisklassen in Deklarationsreihenfolge, gefolgt von den direkten nicht-statischen Datenmembern, die weder anonyme Bitfelder noch Member einer anonymen Union sind, in Deklarationsreihenfolge.
(seit C++17)

[bearbeiten] Zugehörigkeit

Jede Initialisierungsklausel in einer klammergefassten Initialisierungsliste wird gesagt, dass sie einem Element des zu initialisierenden Aggregats oder einem Element eines seiner Unteraggregate zugehört.

Betrachtet man die Sequenz der Initialisierungsklauseln und die Sequenz der zunächst gebildeten Aggregat-Elemente als die Sequenz der Elemente des zu initialisierenden Aggregats und wie unten beschrieben modifiziert werden kann

  • Für jede Initialisierungsklausel, wenn eine der folgenden Bedingungen erfüllt ist, gehört sie zum entsprechenden Aggregat-Element elem
  • elem ist kein Aggregat.
  • Die Initialisierungsklausel beginnt mit {.
  • Die Initialisierungsklausel ist ein Ausdruck, und eine implizite Konversionssequenz kann gebildet werden, die den Ausdruck in den Typ von elem konvertiert.
  • elem ist ein Aggregat, das selbst keine Aggregat-Elemente hat.
  • Andernfalls ist elem ein Aggregat und dieses Unteraggregat wird in der Liste der Aggregat-Elemente durch die Sequenz seiner eigenen Aggregat-Elemente ersetzt, und die Analyse der Zugehörigkeit wird mit dem ersten solchen Element und der gleichen Initialisierungsklausel fortgesetzt. Mit anderen Worten, diese Regeln gelten rekursiv für die Unteraggregate des Aggregats.

Die Analyse ist abgeschlossen, wenn alle Initialisierungsklauseln aufgebraucht sind. Wenn noch Initialisierungsklauseln übrig sind, die keinem Element des Aggregats oder einem seiner Unteraggregate angehören, ist das Programm fehlerhaft.

struct S1 { int a, b; };
struct S2 { S1 s, t; };
 
// Each subaggregate of “x” is appertained to an initializer clause starting with {
S2 x[2] =
{
    // appertains to “x[0]”
    {
        {1, 2}, // appertains to “x[0].s”
        {3, 4}  // appertains to “x[0].t”
    },
    // appertains to “x[1]”
    {
        {5, 6}, // appertains to “x[1].s”
        {7, 8}  // appertains to “x[1].t”
    }
};
 
// “x” and “y” have the same value (see below)
S2 y[2] = {1, 2, 3, 4, 5, 6, 7, 8};
 
// The process of the appertainment analysis of “y”:
// 1. Initializes the aggregate element sequence (x[0], x[1]) and
//    the initializer clause sequence (1, 2, 3, 4, 5, 6, 7, 8).
// 2. Starting from the first elements of each sequence,
//    checks whether 1 appertains to x[0]:
//    · x[0] is an aggregate.
//    · 1 does not begin with {.
//    · 1 is an expression, but it cannot be implicitly converted to S2.
//    · x[0] has aggregate elements.
// 3. 0 cannot appertain to x[0], therefore x[0] is replaced by x[0].s and x[0].t,
//    the aggregate element sequence becomes (x[0].s, x[0].t, x[1]).
// 4. Resumes the appertainment check, but 1 cannot appertain to x[0].s either.
// 5. The aggregate element sequence now becomes (x[0].s.a, x[0].s.b, x[0].t, x[1]).
// 6. Resumes the appertainment check again:
//    1 appertains to x[0].s.a, and 2 appertains to x[0].s.b.
// 7. The rest of the appertainment analysis works similarly.
 
char cv[4] = {'a', 's', 'd', 'f', 0}; // Error: too many initializer clauses

[bearbeiten] Initialisierungsprozess

[bearbeiten] Bestimmung der Elementart

Die Auswirkungen der Aggregatinitialisierung sind

1) Bestimmen Sie die explizit initialisierten Elemente des Aggregats wie folgt:
  • Wenn die Initialisierungsliste eine Bezeichnerinitialisierungsliste ist (das Aggregat kann nur vom Klassentyp sein), muss der Bezeichner in jedem Designator einen direkten nicht-statischen Datenmember der Klasse benennen, und die explizit initialisierten Elemente des Aggregats sind die Elemente, die diese Member enthalten oder sind.
(seit C++20)
  • Andernfalls, wenn die Initialisierungsliste nicht leer ist, sind die explizit initialisierten Elemente des Aggregats die Elemente mit einer zugeordneten Initialisierungsklausel und die Elemente mit einem Unteraggregat mit einer zugeordneten Initialisierungsklausel.
  • Andernfalls muss die Initialisierungsliste leer sein ({}) und es gibt keine explizit initialisierten Elemente.
Das Programm ist fehlerhaft, wenn das Aggregat eine Union ist und es zwei oder mehr explizit initialisierte Elemente gibt.
union u { int a; const char* b; };
 
u a = {1};                   // OK: explicitly initializes member `a`
u b = {0, "asdf"};           // error: explicitly initializes two members
u c = {"asdf"};              // error: int cannot be initialized by "asdf"
 
// C++20 designated initializer lists
u d = {.b = "asdf"};         // OK: can explicitly initialize a non-initial member
u e = {.a = 1, .b = "asdf"}; // error: explicitly initializes two members
2) Initialisieren Sie jedes Element des Aggregats in der Elementreihenfolge. Das heißt, alle Wertberechnungen und Seiteneffekte, die mit einem bestimmten Element verbunden sind, werden sequenziert vor denen jedes Elements, das ihm in der Reihenfolge folgt(seit C++11).

[bearbeiten] Explizit initialisierte Elemente

Für jedes explizit initialisierte Element

  • Wenn das Element ein anonymes Union-Mitglied ist und die Initialisierungsliste eine Bezeichnerinitialisierungsliste ist, wird das Element durch die Bezeichnerinitialisierung {D} initialisiert, wobei D die Bezeichnerinitialisierungsklausel ist, die ein Mitglied des anonymen Union-Mitglieds benennt. Es darf nur eine solche Bezeichnerinitialisierungsklausel 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 Bezeichnerinitialisierungsliste ist, wird das Element mit dem Initialisierer der entsprechenden Bezeichnerinitialisierungsklausel initialisiert.
  • Wenn dieser Initialisierer die Syntax (1) hat und eine Verengungskonvertierung erforderlich ist, um den Ausdruck zu konvertieren, ist das Programm fehlerhaft.
(seit C++20)


  • Die Initialisierungsliste ist eine klammergefasste Initialisierungsliste
(bis C++20)
  • Andernfalls ist die Initialisierungsliste eine nicht-bezeichnete klammergefasste Initialisierungsliste
(seit C++20)
  • Wenn eine Initialisierungsklausel dem Aggregat-Element zugeordnet ist, dann wird das Aggregat-Element aus der Initialisierungsklausel kopierinitialisiert.
  • Andernfalls wird das Aggregat-Element aus einer klammergefassten Initialisierungsliste kopierinitialisiert, die aus allen Initialisierungsklauseln besteht, die Unterobjekten des Aggregat-Elements zugeordnet sind, in der Reihenfolge ihres Erscheinens.
struct A
{
    int x;
 
    struct B
    {
        int i;
        int j;
    } b;
} a = {1, {2, 3}}; // initializes a.x with 1, a.b.i with 2, a.b.j with 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}; // initializes d1.b1 with 1, d1.b2 with 2,
                           //             d1.b3 with 42, d1.d with 4
derived d2{{}, {}, 4};     // initializes d2.b1 with 0, d2.b2 with 42,
                           //             d2.b3 with 42, d2.d with 4

[bearbeiten] Implizit initialisierte Elemente

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

(seit C++11)
  • Andernfalls, wenn das Element keine Referenz ist, wird das Element aus einer leeren Initialisierungsliste kopierinitialisiert.
  • Andernfalls ist das Programm fehlerhaft.
struct S
{
    int a;
    const char* b;
    int c;
    int d = b[a];
};
 
// initializes ss.a with 1,
//             ss.b with "asdf",
//             ss.c with the value of an expression of the form int{} (that is, 0),
//         and ss.d with the value of ss.b[ss.a] (that is, 's')
S ss = {1, "asdf"};

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

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

[bearbeiten] Arrays mit unbekannter Größe

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

int x[] = {1, 3, 5}; // x has 3 elements
 
struct Y { int i, j, k; };
 
Y y[] = {1, 2, 3, 4, 5, 6}; // y has only 2 elements:
                            // 1, 2 and 3 appertain to y[0],
                            // 4, 5 and 6 appertain to y[1]
 
int z[] = {} // Error: cannot declare an array without any element

Bezeichner-Initialisierer

Die Syntaxformen (3,4) werden als Bezeichnerinitialisierer bezeichnet: Jeder Bezeichner muss einen direkten nicht-statischen Datenmember von T benennen, und alle im Ausdruck verwendeten Bezeichner müssen in derselben Reihenfolge erscheinen wie die Datenmember von T.

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

Jeder direkte nicht-statische Datenmember, der vom Bezeichnerinitialisierer benannt wird, wird aus dem entsprechenden klammer-oder-Gleichheits-Initialisierer, der dem Bezeichner folgt, initialisiert. Verengungskonvertierungen sind verboten.

Bezeichnerinitialisierer können verwendet werden, um eine Union in einen anderen Zustand als den ersten zu initialisieren. Für eine Union darf nur ein Initialisierer bereitgestellt 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 Bezeichnerinitialisierer bereitgestellt wird, wie oben beschrieben für den Fall, dass die Anzahl der Initialisierungsklauseln geringer ist als die Anzahl der Member (Standard-Memberinitialisierer, falls vorhanden, ansonsten leere Listeninitialisierung) initialisiert.

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

Wenn das mit einer Bezeichnerinitialisierungsklausel initialisierte Aggregat ein anonymes Union-Mitglied hat, muss der entsprechende Bezeichner ein Mitglied dieser anonymen Union benennen.

Hinweis: Nicht sortierte Bezeichnerinitialisierung, verschachtelte Bezeichnerinitialisierung, Vermischung von Bezeichnerinitialisierern und regulären Initialisierern sowie Bezeichnerinitialisierung von Arrays werden in der C-Programmiersprache unterstützt, sind aber 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)

[bearbeiten] Zeichen-Arrays

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 aus gewöhnlichen String-Literalen, UTF-8-String-Literalen(seit C++20), UTF-16-String-Literalen, UTF-32-String-Literalen(seit C++11), oder breiten String-Literalen, jeweils optional in Klammern eingeschlossen, initialisiert werden. Zusätzlich kann ein Array aus char oder unsigned char durch ein UTF-8-String-Literal, optional in Klammern eingeschlossen, initialisiert werden(seit C++20). Sukzessive Zeichen des String-Literals (einschließlich des impliziten abschließenden Nullzeichens) initialisieren die Elemente des Arrays, mit einer Ganzzahlkonvertierung, falls für Wert der Quelle und des Ziels erforderlich(seit C++20). Wenn die Größe des Arrays angegeben ist und größer ist als die Anzahl der Zeichen im String-Literal, werden die verbleibenden Zeichen Null-initialisiert.

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'};

[bearbeiten] Hinweise

Eine Aggregatklasse oder ein Aggregat-Array kann Nicht-Aggregat- öffentliche Basen(seit C++17), Member oder Elemente enthalten, die wie oben beschrieben initialisiert werden (z. B. Kopierinitialisierung aus der entsprechenden Initialisierungsklausel).

Bis C++11 waren Verengungskonvertierungen bei der Aggregatinitialisierung zulässig, aber sie sind nicht mehr erlaubt.

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

In C kann ein Zeichen-Array der Größe eins kleiner als die Größe des String-Literals aus einem String-Literal initialisiert werden; das resultierende Array ist nicht null-terminiert. Dies ist in C++ nicht erlaubt.

Feature-Testmakro Wert Std Feature
__cpp_aggregate_bases 201603L (C++17) Aggregatklassen mit Basisklassen
__cpp_aggregate_nsdmi 201304L (C++14) Aggregatklassen mit Standard-Memberinitialisierern
__cpp_aggregate_paren_init 201902L (C++20) Aggregatinitialisierung in Form der Direktinitialisierung
__cpp_char8_t 202207L (C++23)
(DR20)
char8_t Kompatibilitäts- und Portabilitätsfix (Initialisierung von (unsigned char-Arrays) mit UTF-8-String-Literalen erlauben)
__cpp_designated_initializers 201707L (C++20) Bezeichner-Initialisierer

[bearbeiten] 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}; // 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 until CWG 1270:
                            // brace elision only allowed with equals sign
 
    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
    union U
    {
        int a;
        const char* b;
    };
    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
 
    [](...) { std::puts("Garbage collecting unused variables... Done."); }
    (
        s1, s2, s3, s4, ar, cr, ar2d1, ar2d2, ar2d3, std_ar2, std_ar1, u1
    );
}
 
// 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

Ausgabe

Garbage collecting unused variables... Done.

[bearbeiten] 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 Zeichen-Array mit einem String-Literal initialisiert wird
mit weniger Zeichen als die Array-Größe, die Zeichen
Elemente nach dem abschließenden '\0' waren nicht initialisiert
sie sind
Null-initialisiert
CWG 1270 C++11 Klammerauslassung durfte nur in der Kopier-Listeninitialisierung verwendet werden woanders erlaubt
CWG 1518 C++11 eine Klasse, die einen expliziten Standardkonstruktor deklariert oder
vererbte Konstruktoren hat, könnte ein Aggregat sein
es ist kein
Aggregat
CWG 1622 C++98 eine Union konnte nicht mit {} initialisiert werden erlaubt
CWG 2149
(P3106R1)
C++98 Es war unklar, ob die Klammerauslassung
anwendbar ist bei der Array-Größenermittlung
anwendbar
CWG 2272 C++98 ein nicht-statisches Referenzmitglied, das nicht explizit
initialisiert wurde, wurde aus einer leeren Initialisierungsliste kopierinitialisiert
das Programm ist schlecht ge-
formt in diesem Fall
CWG 2610 C++17 Aggregattypen konnten keine privaten oder geschützten indirekten Basisklassen haben erlaubt
CWG 2619 C++20 Die Art der Initialisierung aus Bezeichnerinitialisierern war unklar es hängt von der
Art des Initialisierers ab
P2513R4 C++20 Ein UTF-8-String-Literal konnte kein Array von char initialisieren
oder unsigned char, was inkompatibel mit C oder C++17 war
solche Initialisierung
ist gültig

[bearbeiten] Siehe auch

C-Dokumentation für Initialisierung von Strukturen und Unions