Warum unterscheidet sich das Verhalten von C ++ initializer_list für std :: vector und std :: array?

74

Code:

std::vector<int> x{1,2,3,4};
std::array<int, 4> y{{1,2,3,4}};

Warum brauche ich doppelte geschweifte Klammern für std :: array?

Sungmin
quelle
1
Benötigen Sie tatsächlich den zweiten Satz Zahnspangen std::arrayoder erhalten Sie nur eine Warnung? std::array<int,4> y{1,2,3,4};funktioniert bei mir.
Bames53
3
@ bames53: GCC ist falsch beim Kompilieren.
Xeo
14
@Xeo: Es ist nicht "falsch", ein schlecht geformtes Programm mit einer Warnung zu kompilieren.
Steve Jessop
3
@Steve: Stimmt das. Sagen wir nicht tragbar?
Xeo
2
@Xeo: Ja, ich verwende es -Werrorsowieso für Code, den ich geschrieben habe, also schadet es meiner Portabilität nicht. Die Laufleistung anderer kann variieren, wenn sie Leichtgewichte sind oder Header-Dateien enthalten müssen, die von Leichtgewichten geschrieben wurden :-)
Steve Jessop

Antworten:

63

std::array<T, N>ist ein Aggregat: Es gibt keine vom Benutzer deklarierten Konstruktoren, nicht einmal einen, der a nimmt std::initializer_list. Die Initialisierung mit geschweiften Klammern erfolgt mit der aggregierten Initialisierung , einer Funktion von C ++, die von C geerbt wurde.

Der "alte Stil" der Aggregatinitialisierung verwendet Folgendes =:

std::array<int, 4> y = { { 1, 2, 3, 4 } };

Bei dieser alten Art der Aggregatinitialisierung können zusätzliche Klammern entfernt werden. Dies entspricht also:

std::array<int, 4> y = { 1, 2, 3, 4 };

Diese zusätzlichen Klammern dürfen jedoch nur "in einer Deklaration der Form T x = { a };" (C ++ 11 §8.5.1 / 11) entfernt werden, dh wenn der alte Stil =verwendet wird. Diese Regel, die die Klammerelision zulässt, gilt nicht für die direkte Listeninitialisierung. Eine Fußnote lautet hier: "Klammern können bei anderen Verwendungen der Listeninitialisierung nicht entfernt werden."

Zu dieser Einschränkung gibt es einen Fehlerbericht: CWG-Fehler Nr. 1270 . Wenn die vorgeschlagene Entschließung angenommen wird, ist die Klammerentfernung für andere Formen der Listeninitialisierung zulässig, und Folgendes ist wohlgeformt:

std::array<int, 4> y{ 1, 2, 3, 4 };

(Hutspitze an Ville Voutilainen, um den Fehlerbericht zu finden.)

James McNellis
quelle
3
Es ist also ein Fehler des präsentierten Abstraktionsmodells array?
Mark Ransom
1
@Mehrdad: Es ist einheitlich. Was nicht einheitlich ist, ist die Tatsache, dass Sie zwei völlig unterschiedliche Typen initialisieren.
Nicol Bolas
5
@NicolBolas: Ich dachte, der springende Punkt der Einheitlichkeit wäre, die gleiche Initialisierungssyntax für verschiedene Typen zu verwenden? (Ja, ich verstehe, was passiert ... Ich sage nur, dass es für den Benutzer nicht "einheitlich" ist, unabhängig davon, ob es eine Erklärung dafür gibt.)
user541686
@MarkRansom: Nun, es ist eher eine Eigenart der Sprache, da es std::array<int> y = {1,2,3,4};mit einer Warnung von Clang arbeitet, die Klammern vorschlägt , anstatt eines schweren Fehlers darüber, dass bei Verwendung der direkten Listeninitialisierung keine Klammern um die Initialisierung des Unterobjekts weggelassen werden dürfen.
Xeo
1
@Xeo: Tatsächlich ist die Klammerelision bei der aggregierten Initialisierung zulässig, jedoch (anscheinend) nicht, wenn die Syntax für die direkte Listeninitialisierung verwendet wird.
James McNellis
29

Weil std::vectorbietet einen Konstruktor, der a aufnimmt std::initializer_list<T>, während er std::arraykeine Konstruktoren hat und die {1, 2, 3, 4}geschweifte Init-Liste tatsächlich nicht als std::initializer_list, sondern als aggregierte Initialisierung für das innere C-artige Array von interpretiert wird std::array(daher kommt der zweite Satz von Klammern: Eins für std::arrayeine für das innere C-Style-Member-Array).

Xeo
quelle