Kurzversion: Eine Initialisierungsklausel, die mit {
beginnt, stoppt brace-elision. Dies ist der Fall im ersten Beispiel mit {1,2}
, aber nicht in der dritten oder vierten, die A{1,2}
verwenden. Brace-elision verbraucht die nächsten N Initialisierer-Klauseln (wobei N von dem zu initialisierenden Aggregat abhängt), weshalb nur die erste Initialisierer-Klausel des N nicht mit {
beginnen darf.
In allen Implementierungen der C++ Standard Library ich kenne, std::array
ist eine Struktur, die eine C-Stil-Array enthält. Das heißt, Sie ein Aggregat haben, die enthält eine Unter Aggregat, ähnlich wie
template<typename T, std::size_t N>
struct array
{
T __arr[N]; // don't access this directly!
};
Wenn ein std::array
von einem verspannt-init-Liste Initialisieren Sie daher die Mitglieder der initialisieren müssen enthaltenes Array. Daher wird auf diesen Implementierungen kann die explizite Form ist:
std::array<A, 4> x = {{ {1,2}, {3,4}, {5,6}, {7,8} }};
Die äußerste Reihe von Klammern bezieht sich auf die std::array
struct; Die zweite Gruppe von geschweiften Klammern bezieht sich auf das verschachtelte C-artige Array.
C++ ermöglicht in Aggregat-Initialisierung, bestimmte geschweifte Klammern beim Initialisieren verschachtelter Aggregate wegzulassen. Zum Beispiel:
struct outer {
struct inner {
int i;
};
inner x;
};
outer e = { { 42 } }; // explicit braces
outer o = { 42 }; // with brace-elision
Die Regeln sind wie folgt (eine Post-N4527 Entwurf verwendet wird, die post-C++ 14, aber 11 C++ einen Defekt enthalten auf diese ohnehin bezogen):
Klammern können in einer Initialisierungsliste wie folgt entfernt werden. Wenn die Initialisierer-Liste mit einer linken Klammer beginnt, dann die nachfolgenden durch Kommata getrennte Liste von initializer-Klauseln initialisiert die Mitglieder der ein Unteraggregat; es ist falsch, dass es mehr Initialisierungsklauseln als Mitglieder gibt.Wenn jedoch die Initialisierer-Liste für eine Unteraggregat beginnt nicht mit einer linken Klammer, dann nur genug initializer-Klauseln aus der Liste genommen, um die Mitglieder des Unteraggregats zu initialisieren; alle verbleibenden initializer-Klauseln sind übrig, um das nächste Element des Aggregats zu initialisieren, von dem das aktuelle Unteraggregat ein Mitglied ist.
Angewandt auf dem ersten std::array
-Beispiel:
static std::array<A, 4> x1 =
{
{ 1, 2 },
{ 3, 4 },
{ 5, 6 },
{ 7, 8 }
};
Dies wird wie folgt interpretiert:
static std::array<A, 4> x1 =
{ // x1 {
{ // __arr {
1, // __arr[0]
2 // __arr[1]
// __arr[2] = {}
// __arr[3] = {}
} // }
{3,4}, // ??
{5,6}, // ??
...
}; // }
Die erste {
als initializer der std::array
Struktur genommen wird. Die Initialisiererklauseln{1,2}, {3,4}
usw. werden dann als die Initialisierer der Unteraggregate von std::array
genommen. Beachten Sie, dass nur ein einziges Unteraggregat __arr
hat. Seit der ersten initializer-Klausel{1,2}
beginnt mit einem {
, die Klammer-elision Ausnahme nicht auftritt, und der Compiler versucht, die verschachtelten A __arr[4]
Array mit {1,2}
zu initialisieren. Die restlichen Initialisierungsklauseln{3,4}, {5,6}
usw. beziehen sich nicht auf Unteraggregate std::array
und sind daher illegal.
Im dritten und vierten Beispiel ist die erste initializer-Klausel für das Unteraggregat von std::array
nicht mit einem {
beginnen, damit die Klammer elision Ausnahme angewandt wird:
static std::array<A, 4> x4 =
{
A{ 1, 2 }, // does not begin with {
{ 3, 4 },
{ 5, 6 },
{ 7, 8 }
};
So ist es wie folgt interpretiert:
static std::array<A, 4> x4 =
{ // x4 {
// __arr { -- brace elided
A{ 1, 2 }, // __arr[0]
{ 3, 4 }, // __arr[1]
{ 5, 6 }, // __arr[2]
{ 7, 8 } // __arr[3]
// } -- brace elided
}; // }
Daher sind die A{1,2}
bewirkt, dass alle vier initializer-Klauseln werden verbraucht, um das verschachtelte C-artige Array zu initialisieren. Wenn Sie einen anderen initializer hinzufügen:
static std::array<A, 4> x4 =
{
A{ 1, 2 }, // does not begin with {
{ 3, 4 },
{ 5, 6 },
{ 7, 8 },
X
};
dann diese X
würde verwendet werden, um die nächste Unteraggregat von std::array
zu initialisieren. Z.B.
struct outer {
struct inner {
int a;
int b;
};
inner i;
int c;
};
outer o =
{ // o {
// i {
1, // a
2, // b
// }
3 // c
}; // }
Brace-elision verbraucht der nächsten N-Initialisierer-Klauseln, wobei N durch die Anzahl von Initialisierungen für die (Unter-) Aggregat erforderlich ist definiert initialisiert werden. Daher ist es nur wichtig, ob die erste dieser N Initialisierungsklauseln mit einer {
beginnt oder nicht.
Weitere ähnlich zu dem OP:
struct inner {
int a;
int b;
};
struct outer {
struct middle {
inner i;
};
middle m;
int c;
};
outer o =
{ // o {
// m {
inner{1,2}, // i
// }
3 // c
}; // }
anzumerken, dass spangen elision rekursiv gilt; wir können sogar die verwirrende
outer o =
{ // o {
// m {
// i {
1, // a
2, // b
// }
// }
3 // c
}; // }
schreiben, wo wir beide die Zahnspange für o.m
und o.m.i
wegzulassen. Die ersten beiden Initialisierer-Klauseln werden verbraucht, um o.m.i
zu initialisieren, der verbleibende initialisiert o.c
. Sobald wir ein Paar von Klammern um 1,2
einsetzen, wird es als das Paar von Klammern interpretiert entsprechend o.m
:
outer o =
{ // o {
{ // m {
// i {
1, // a
2, // b
// }
} // }
3 // c
}; // }
Hier wird der Initialisierer für o.m
mit einem {
beginnen, damit verspannen-elision keine Anwendung findet. Der Initialisierer für o.m.i
ist 1
, der nicht mit {
beginnt, daher wird für o.m.i
die Klammerelektion angewendet und die beiden Initialisierer 1
und 2
werden verbraucht.
Es tut mir leid, aber ich kann nicht sehen, warum die erste Aufgabe nicht kompiliert, können Sie mir bitte mehr sagen? Es ist interessant ! – Telokis
@Ninetainedo - siehe die verknüpfte Frage. – Jeremy
@dyp - Korrigiert. – Jeremy