Aggregate initialization
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) | |||||||
Definitionen
Aggregat
Ein aggregate ist einer der folgenden Typen:
- Array-Typen
- Klassentypen, die
|
(bis C++11) |
|
(seit C++11)
(bis C++20) |
|
(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
|
(bis C++17) |
|
(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:
|
(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
Explizit initialisierte Elemente
Für jedes explizit initialisierte Element:
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
|
(seit C++20) |
|
(until C++20) |
|
(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
|
(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 initializersDie 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
|