Namespaces
Variants

List-initialization (since C++11)

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 Objekt aus einer geschweiften Klammer-Initialisierungsliste .

Inhaltsverzeichnis

Syntax

Direkte Listeninitialisierung

T Objekt { arg1, arg2, ... };

T Objekt {. des1 = arg1 , . des2 { arg2 } ... };

(seit C++20)
(1)
T { arg1, arg2, ... }

T {. des1 = arg1 , . des2 { arg2 } ... }

(seit C++20)
(2)
new T { arg1, arg2, ... }

new T {. des1 = arg1 , . des2 { arg2 } ... }

(seit C++20)
(3)
Klasse { T Mitglied { arg1, arg2, ... }; };

Klasse { T Mitglied {. des1 = arg1 , . des2 { arg2 } ... }; };

(seit C++20)
(4)
Klasse :: Klasse () : Mitglied { arg1, arg2, ... } {...

Klasse :: Klasse () : Mitglied {. des1 = arg1 , . des2 { arg2 } ... } {...

(seit C++20)
(5)

Copy-Listeninitialisierung

T Objekt = { arg1, arg2, ... };

T Objekt = {. des1 = arg1 , . des2 { arg2 } ... };

(seit C++20)
(6)
Funktion ({ arg1, arg2, ... })

Funktion ({. des1 = arg1 , . des2 { arg2 } ... })

(seit C++20)
(7)
return { arg1, arg2, ... };

return {. des1 = arg1 , . des2 { arg2 } ... };

(seit C++20)
(8)
Objekt [{ arg1, arg2, ... }]

Objekt [{. des1 = arg1 , . des2 { arg2 } ... }]

(seit C++20)
(9)
Objekt = { arg1, arg2, ... }

Objekt = {. des1 = arg1 , . des2 { arg2 } ... }

(seit C++20)
(10)
U ({ arg1, arg2, ... })

U ({. des1 = arg1 , . des2 { arg2 } ... })

(seit C++20)
(11)
Klasse { T Mitglied = { arg1, arg2, ... }; };

Klasse { T Mitglied = {. des1 = arg1 , . des2 { arg2 } ... }; };

(seit C++20)
(12)

Die Listeninitialisierung wird in den folgenden Situationen durchgeführt:

  • direkte Listeninitialisierung (sowohl explizite als auch nicht-explizite Konstruktoren werden berücksichtigt)
1) Initialisierung einer benannten Variable mit einer geschweiften Klammer-Initialisierungsliste
2) Initialisierung eines unbenannten Temporärs mit einer geschweiften Klammer-Initialisierungsliste
3) Initialisierung eines Objekts mit dynamischer Speicherdauer durch einen new-expression , wobei der Initialisierer eine geschweifte Klammer-Initialisiererliste ist
4) in einem nicht-statischen data member initializer , der das Gleichheitszeichen nicht verwendet
5) in einer Member-Initialisierungsliste eines Konstruktors, wenn eine geschweifte Klammer-Initialisierungsliste verwendet wird
  • Copy-List-Initialisierung (sowohl explizite als auch nicht-explizite Konstruktoren werden berücksichtigt, aber nur nicht-explizite Konstruktoren können aufgerufen werden)
6) Initialisierung einer benannten Variable mit einer geschweiften Klammer umschlossenen Initialisierungsliste nach einem Gleichheitszeichen
7) in einem Funktionsaufrufausdruck, bei dem eine geschweifte Klammer-Initialisierungsliste als Argument verwendet wird und die Listeninitialisierung den Funktionsparameter initialisiert
8) in einer return -Anweisung mit einer in geschweiften Klammern eingeschlossenen Initialisierungsliste, die als Rückgabeausdruck verwendet wird und die Listeninitialisierung das zurückgegebene Objekt initialisiert
9) in einem Subscript-Ausdruck mit einem benutzerdefinierten operator[] , wo die Listeninitialisierung den Parameter des überladenen Operators initialisiert
10) in einem assignment expression , bei dem die Listeninitialisierung den Parameter des überladenen Operators initialisiert
11) Functional-Cast-Ausdruck oder andere Konstruktoraufrufe, bei denen eine geschweifte Klammer-Initialisierungsliste anstelle eines Konstruktorarguments verwendet wird. Copy-List-Initialisierung initialisiert den Parameter des Konstruktors (Hinweis: Der Typ U in diesem Beispiel ist nicht der Typ, der listeninitialisiert wird; U 's Konstruktorparameter ist es)
12) in einem nicht-statischen data member initializer , der das Gleichheitszeichen verwendet

Erklärung

Die Auswirkungen der Listeninitialisierung eines Objekts vom Typ (möglicherweise cv-qualifiziert) T sind:

  • Wenn die geschweifte Klammer-Initialisierungsliste eine designated initializer list enthält und T kein Referenztyp ist, muss T eine Aggregatklasse sein. Die geordneten Bezeichner in den Designatoren der designated initializer list müssen eine Teilfolge der geordneten Bezeichner in den direkten nicht-statischen Datenelementen von T bilden. Aggregate initialization wird durchgeführt.
(seit C++20)
  • Wenn T eine Aggregatklasse ist und die geschweifte Klammer-Initialisierungsliste , die keine designated initializer list enthält, (seit C++20) eine einzelne Initialisierungsklausel desselben oder abgeleiteten Typs (möglicherweise cv-qualifiziert) enthält, wird das Objekt aus dieser Initialisierungsklausel initialisiert (durch Copy-Initialisierung für Copy-List-Initialisierung oder durch Direct-Initialisierung für Direct-List-Initialisierung).
  • Andernfalls, wenn T ein Zeichenarray ist und die geschweifte Klammer-Initialisierungsliste eine einzelne Initialisierungsklausel enthält, die ein Zeichenkettenliteral mit passendem Typ ist, wird das Array wie üblich vom Zeichenkettenliteral initialisiert .
  • Andernfalls, wenn die geschweifte Klammer-Initialisierungsliste leer ist und T ein Klassentyp mit einem Standardkonstruktor ist, wird Wertinitialisierung durchgeführt.
  • Andernfalls, wenn T ein Klassentyp ist, werden die Konstruktoren von T in zwei Phasen betrachtet:
  • Wenn die vorherige Stufe keine Übereinstimmung ergibt, nehmen alle Konstruktoren von T an der Überlagerungsauflösung gegenüber der Menge der Argumente teil, die aus den Initialisierer-Klauseln der geschweiften Initialisiererliste besteht, mit der Einschränkung, dass nur nicht-verengende Konvertierungen erlaubt sind. Wenn diese Stufe einen expliziten Konstruktor als beste Übereinstimmung für eine Copy-List-Initialisierung ergibt, schlägt die Kompilierung fehl (beachte, bei einfacher Copy-Initialisierung werden explizite Konstruktoren überhaupt nicht berücksichtigt).
  • Andernfalls, falls T ein Aufzählungstyp mit festgelegtem zugrundeliegenden Typ U ist, die geschweifte Klammer-Initialisierungsliste nur einen Initialisierer v enthält und alle folgenden Bedingungen erfüllt sind, dann wird die Aufzählung mit dem Ergebnis der Konvertierung von v zu U initialisiert:
    • Die Initialisierung ist eine Direct-List-Initialisierung.
    • v ist vom skalaren Typ .
    • v ist implizit konvertierbar zu U .
    • Die Konvertierung von v zu U ist nicht einschränkend (non-narrowing).
(seit C++17)
  • Andernfalls (wenn T kein Klassentyp ist), falls die geschweifte Initialisierungsliste nur einen Initialisierungsabschnitt enthält und entweder T kein Referenztyp ist oder ein Referenztyp, dessen referenzierter Typ derselbe wie oder eine Basisklasse des Typs des Initialisierungsabschnitts ist, wird T direkt initialisiert (bei direkter Listeninitialisierung) oder kopierinitialisiert (bei Kopierlisteninitialisierung), außer dass einschränkende Konvertierungen nicht erlaubt sind.
  • Andernfalls, wenn T ein Referenztyp ist, der nicht mit dem Typ des Initialisierer-Ausdrucks kompatibel ist:
  • Ein prvalue-Temporär des durch T referenzierten Typs wird copy-list-initialisiert, und die Referenz wird an dieses Temporär gebunden (dies schlägt fehl, wenn die Referenz eine non-const lvalue-Referenz ist).
(bis C++17)
  • Ein prvalue wird erzeugt. Der prvalue initialisiert sein Ergebnisobjekt durch Copy-List-Initialisierung. Der prvalue wird dann zur Direct-Initialisierung der Referenz verwendet (dies schlägt fehl, wenn die Referenz eine non-const lvalue-Referenz ist). Der Typ des Temporärs ist der durch T referenzierte Typ , außer T ist "Referenz auf Array unbekannter Größe von U ", in welchem Fall der Typ des Temporärs der Typ von x in der Deklaration U x [ ] H ist, wobei H die Initialisierungsliste ist (seit C++20) .
(seit C++17)
  • Andernfalls, wenn die geschweifte Klammer-Initialisiererliste keine Initialisierer-Klausel hat, T wird value-initialized .

Listeninitialisierung von std::initializer_list

Ein Objekt vom Typ std:: initializer_list < E > wird aus einer Initialisierungsliste konstruiert, als ob der Compiler einen materialisierten (seit C++17) Prvalue vom Typ "Array von N const E " erzeugen würde, wobei N die Anzahl der Initialisierungsclauseln in der Initialisierungsliste ist; dies wird als Backing Array der Initialisierungsliste bezeichnet.

Jedes Element des zugrundeliegenden Arrays wird copy-initialisiert mit dem entsprechenden Initialisierer-Ausdruck der Initialisiererliste, und das std:: initializer_list < E > -Objekt wird so konstruiert, dass es auf dieses Array verweist. Ein für den Kopiervorgang ausgewählter Konstruktor oder eine Konvertierungsfunktion muss zugänglich im Kontext der Initialisiererliste sein. Wenn eine einschränkende Konvertierung zur Initialisierung eines der Elemente erforderlich ist, ist das Programm fehlerhaft.

Das zugrundeliegende Array hat die gleiche Lebensdauer wie jedes andere temporäre Objekt , außer dass die Initialisierung eines std::initializer_list -Objekts aus dem zugrundeliegenden Array die Lebensdauer des Arrays genau wie das Binden einer Referenz an ein temporäres Objekt verlängert.

void f(std::initializer_list<double> il);
void g(float x)
{
   f({1, x, 3});
}
void h()
{
   f({1, 2, 3});
}
struct A { mutable int i; };
void q(std::initializer_list<A>);
void r()
{
    q({A{1}, A{2}, A{3}});
}
// Die obige Initialisierung wird in etwa äquivalent zu unten implementiert,
// unter der Annahme, dass der Compiler ein initializer_list-Objekt mit einem Paar von
// Zeigern konstruieren kann, und mit dem Verständnis, dass `__b` den Aufruf von `f` nicht überlebt.
void g(float x)
{
    const double __a[3] = {double{1}, double{x}, double{3}}; // Backing-Array
    f(std::initializer_list<double>(__a, __a + 3));
}
void h()
{
    static constexpr double __b[3] =
        {double{1}, double{2}, double{3}}; // Backing-Array
    f(std::initializer_list<double>(__b, __b + 3));
}
void r()
{
    const A __c[3] = {A{1}, A{2}, A{3}}; // Backing-Array
    q(std::initializer_list<A>(__c, __c + 3));
}

Ob alle zugrundeliegenden Arrays unterschiedlich sind (d. h. in nicht-überlappenden Objekten gespeichert sind), ist nicht spezifiziert:

bool fun(std::initializer_list<int> il1, std::initializer_list<int> il2)
{
    return il2.begin() == il1.begin() + 1;
}
bool overlapping = fun({1, 2, 3}, {2, 3, 4}); // das Ergebnis ist nicht spezifiziert:
                                              // die zugrundeliegenden Arrays können
                                              // Speicher innerhalb von {1, 2, 3, 4} teilen

Einschränkende Konvertierungen

Die Listeninitialisierung beschränkt die zulässigen impliziten Konvertierungen , indem folgende verboten werden:

  • Umwandlung von einem Gleitkommatyp in einen Ganzzahltyp
  • Konvertierung von einem Gleitkommatyp T zu einem anderen Gleitkommatyp, dessen floating-point conversion rank weder größer als noch gleich dem von T ist, außer wenn das Konvertierungsergebnis ein constant expression ist und eine der folgenden Bedingungen erfüllt ist:
    • Der konvertierte Wert ist endlich und die Konvertierung führt nicht zu einem Überlauf.
    • Die Werte vor und nach der Konvertierung sind nicht endlich.
  • Konvertierung von einem Ganzzahltyp zu einem Gleitkommatyp, außer wenn die Quelle ein konstanter Ausdruck ist, dessen Wert exakt im Zieltyp gespeichert werden kann
  • Konvertierung von einem Integer- oder unbegrenzten Enumerationstyp zu einem Integer-Typ, der nicht alle Werte des Originals darstellen kann, außer wenn
    • die Quelle ein Bitfeld ist, dessen Breite w geringer ist als die seines Typs (oder, für einen Enumerationstyp , seines zugrundeliegenden Typs) und der Zieltyp alle Werte eines hypothetischen erweiterten Integer-Typs mit Breite w und mit der gleichen Vorzeichenbehaftung wie der Originaltyp darstellen kann, oder
    • die Quelle ein konstanter Ausdruck ist, dessen Wert exakt im Zieltyp gespeichert werden kann
  • Umwandlung von einem Zeigertyp oder Zeiger-auf-Member-Typ zu bool

Hinweise

Jeder Initialisierer-Ausdruck ist sequenced before jedem Initialisierer-Ausdruck, der ihm in der geschweiften Initialisiererliste folgt. Dies steht im Gegensatz zu den Argumenten eines function call expression , die unsequenced (until C++17) indeterminately sequenced (since C++17) sind.

Eine in geschweiften Klammern eingeschlossene Initialisierungsliste ist kein Ausdruck und hat daher keinen Typ, z.B. decltype ( { 1 , 2 } ) ist fehlerhaft. Das Fehlen eines Typs bedeutet, dass die Template-Typableitung keinen Typ ableiten kann, der einer in geschweiften Klammern eingeschlossenen Initialisierungsliste entspricht, daher ist bei der Deklaration template < class T > void f ( T ) ; der Ausdruck f ( { 1 , 2 , 3 } ) fehlerhaft. Allerdings kann der Template-Parameter anderweitig abgeleitet werden, wie im Fall von std:: vector < int > v ( std:: istream_iterator < int > ( std:: cin ) , { } ) , wo der Iteratortyp durch das erste Argument abgeleitet wird, aber auch in der zweiten Parameterposition verwendet wird. Eine spezielle Ausnahme wird für Typableitung mit dem Schlüsselwort auto gemacht, das jede in geschweiften Klammern eingeschlossene Initialisierungsliste in der Copy-List-Initialisierung als std::initializer_list ableitet.

Auch weil eine in geschweifte Klammern eingeschlossene Initialisierungsliste keinen Typ hat, gelten spezielle Regeln für Overload Resolution , wenn sie als Argument für einen überladenen Funktionsaufruf verwendet wird.

Aggregate führen Copy-/Move-Initialisierung direkt aus einer geschweiften Initialisierungsliste mit einer einzelnen Initialisierungsklausel desselben Typs durch, aber Nicht-Aggregate berücksichtigen zuerst std::initializer_list Konstruktoren:

struct X {}; // Aggregat
struct Q     // Nicht-Aggregat
{
    Q() = default;
    Q(Q const&) = default;
    Q(std::initializer_list<Q>) {}
};
int main()
{
    X x;
    X x2 = X{x}; // Copy-Konstruktor (keine Aggregat-Initialisierung)
    Q q;
    Q q2 = Q{q}; // Initializer-List-Konstruktor (kein Copy-Konstruktor)
}

Einige Compiler (z.B. gcc 10) betrachten nur die Konvertierung von einem Zeiger oder einem Zeiger-auf-Mitglied zu bool als Einengung im C++20-Modus.

Feature-Test-Makro Wert Std Feature
__cpp_initializer_lists 200806L (C++11) Listeninitialisierung und std::initializer_list

Beispiel

#include <iostream>
#include <map>
#include <string>
#include <vector>
struct Foo
{
    std::vector<int> mem = {1, 2, 3}; // Listeninitialisierung eines nicht-statischen Members
    std::vector<int> mem2;
    Foo() : mem2{-1, -2, -3} {} // Listeninitialisierung eines Members im Konstruktor
};
std::pair<std::string, std::string> f(std::pair<std::string, std::string> p)
{
    return {p.second, p.first}; // Listeninitialisierung in return-Anweisung
}
int main()
{
    int n0{};  // Wertinitialisierung (auf Null)
    int n1{1}; // direkte Listeninitialisierung
    std::string s1{'a', 'b', 'c', 'd'}; // Initializer-List-Konstruktoraufruf
    std::string s2{s1, 2, 2};           // regulärer Konstruktoraufruf
    std::string s3{0x61, 'a'}; // Initializer-List-Konstruktor wird (int, char) vorgezogen
    int n2 = {1}; // Kopier-Listeninitialisierung
    double d = double{1.2}; // Listeninitialisierung eines PR-Werts, dann Kopierinitialisierung
    auto s4 = std::string{"HelloWorld"}; // wie oben, kein temporäres Objekt
                                         // erzeugt seit C++17
    std::map<int, std::string> m = // verschachtelte Listeninitialisierung
    {
        {1, "a"},
        {2, {'a', 'b', 'c'}},
        {3, s1}
    };
    std::cout << f({"hello", "world"}).first // Listeninitialisierung im Funktionsaufruf
              << '\n';
    const int (&ar)[2] = {1, 2}; // bindet eine Lvalue-Referenz an ein temporäres Array
    int&& r1 = {1}; // bindet eine Rvalue-Referenz an einen temporären int
//  int& r2 = {2}; // Fehler: kann Rvalue nicht an nicht-konstante Lvalue-Referenz binden
//  int bad{1.0}; // Fehler: einschränkende Konvertierung
    unsigned char uc1{10}; // okay
//  unsigned char uc2{-1}; // Fehler: einschränkende Konvertierung
    Foo f;
    std::cout << n0 << ' ' << n1 << ' ' << n2 << '\n'
              << s1 << ' ' << s2 << ' ' << s3 << '\n';
    for (auto p : m)
        std::cout << p.first << ' ' << p.second << '\n';
    for (auto n : f.mem)
        std::cout << n << ' ';
    for (auto n : f.mem2)
        std::cout << n << ' ';
    std::cout << '\n';
    [](...){}(d, ar, r1, uc1); // hat Effekt von [[maybe_unused]]
}

Ausgabe:

world
0 1 1
abcd cd aa
1 a
2 abc
3 abcd
1 2 3 -1 -2 -3

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 1288 C++11 Die Listeninitialisierung einer Referenz mit einer geschweiften Initialisiererliste
mit einem einzelnen Initialisierer band die Referenz immer an ein Temporärobjekt
Bindung an diesen Initialisierer
wenn gültig
CWG 1290 C++11 Die Lebensdauer des Backing-Arrays war nicht korrekt spezifiziert Spezifiziert wie andere
Temporärobjekte
CWG 1324 C++11 Initialisierung wurde zuerst für die Initialisierung von {} betrachtet Aggregate-Initialisierung
wird zuerst betrachtet
CWG 1418 C++11 Dem Typ des Backing-Arrays fehlte const const hinzugefügt
CWG 1467 C++11 Gleich-Typ-Initialisierung von Aggregaten und Character-
Arrays war verboten; Initializer-List-Konstruktoren hatten
Priorität über Kopierkonstruktoren für Einzelklausel-Listen
Gleich-Typ-Initialisierung
erlaubt; Einzelklausel-
Listen initialisieren direkt
CWG 1494 C++11 Bei Listeninitialisierung einer Referenz mit einem Initialisierer
inkompatiblen Typs war unklar, ob das Temporärobjekt
direkt-listeninitialisiert oder kopier-listeninitialisiert wird
Es hängt von der Art
der Initialisierung
für die Referenz ab
CWG 2137 C++11 Initializer-List-Konstruktoren verloren gegen Kopie-
konstruktoren bei Listeninitialisierung von X aus {X}
Nicht-Aggregate betrachten
Initializer-Listen zuerst
CWG 2252 C++17 Aufzählungen konnten von Nicht-Skalarwerten listeninitialisiert werden Verboten
CWG 2267 C++11 Die Lösung von CWG Issue 1494 machte klar,
dass Temporärobjekte direkt-listeninitialisiert werden könnten
Sie werden kopier-listeninitialisiert
bei Listeninitialisierung von Referenzen
CWG 2374 C++17 Direkt-Listeninitialisierung einer Enum erlaubte zu viele Quelltypen Eingeschränkt
CWG 2627 C++11 Ein schmales Bitfeld eines größeren Integer-Typs kann zu
einem kleineren Integer-Typ promoviert werden, aber es war trotzdem eine Verengungskonvertierung
Es ist keine
Verengungskonvertierung
CWG 2713 C++20 Referenzen auf Aggregate-Klassen konnten nicht
durch Designated-Initializer-Listen initialisiert werden
Erlaubt
CWG 2830 C++11 Listeninitialisierung ignorierte nicht die Top-Level-cv-Qualifikation Ignoriert
CWG 2864 C++11 Fließkomma-Konvertierungen, die überlaufen, waren keine Verengungen Sie sind Verengungen
P1957R2 C++11 Konvertierung von einem Zeiger/Zeiger-auf-Mitglied
zu bool war keine Verengung
Als Verengung betrachtet
P2752R3 C++11 Backing-Arrays mit überlappender Lebensdauer konnten nicht überlappen Sie können überlappen

Siehe auch