Initialisierungsunterschied mit oder ohne geschweiften Klammern in C++

Lesezeit: 5 Minuten

Benutzer-Avatar
TechSpaß

Wir können die Variable in C++ auf zwei Arten initialisieren

Einer:

int abc = 7;

Zwei:

int abc {7};

Was ist der Unterschied zwischen diesen beiden Methoden? Behandelt der Compiler sie anders, oder gibt es einen Unterschied in der Art und Weise, wie der Code ausgeführt wird?

  • en.cppreference.com/w/cpp/language/initialization

    – Chris

    15. Januar 2014 um 23:08 Uhr


  • Zum int Der einzige Unterschied besteht insbesondere darin, dass {} verbietet eine einschränkende Konvertierung, z. B. von einem Literal, das zu groß ist, um in ein zu passen int. Bei Klassentypen kann es kompliziert werden, je nachdem, wie detailliert Sie die Unterschiede kennen möchten.

    – dyp

    15. Januar 2014 um 23:09 Uhr

Kurzfassung

Initialisierung über {..} ist die Listeninitialisierung, die einschränkende Konvertierungen verbietet. Zum Beispiel, wenn LLONG_MAX ist der Maximalwert von an long long intund dein int kann das nicht darstellen:

int x = LLONG_MAX;  // probably accepted with a warning
int x {LLONG_MAX};  // error

Ähnlich:

long long y = /*something*/;

int x = y;  // accepted, maybe with a warning
int x {y};  // error

Lange Version

Eine Initialisierung des Formulars

T x = a;

ist Kopie-Initialisierung; eine Initialisierung einer der beiden Formen

T x(a);
T x{a};

ist Direkt-Initialisierung, [dcl.init]/15-16.

[dcl.init]/14 sagt dann:

Die Form der Initialisierung (mit Klammern bzw =) ist im Allgemeinen unbedeutend, spielt aber eine Rolle, wenn der Initialisierer oder die zu initialisierende Entität einen Klassentyp hat; siehe unten.

Also für Nicht-Klassen-Typen, die bilden der Initialisierung spielt keine Rolle. Es gibt jedoch einen Unterschied zwischen diesen beiden direkten Initialisierungen:

T x(a);  // 1
T x{a};  // 2

und ähnlich zwischen diesen beiden Kopierinitialisierungen:

T x = a;    // 1
T x = {a};  // 2

Nämlich die mit {..} Verwenden Sie die Listeninitialisierung. Das {..} heißt ein geklammerte Init-Liste.

Also, wenn Sie vergleichen T x = a; zu T x {a};es gibt zwei Unterschiede: Copy- vs. Direct-Initialisierung und “Non-List-” vs. List-Initialisierung. Wie bereits von anderen und im obigen Zitat erwähnt, für Nicht-Klassen-Typen T, gibt es keinen Unterschied zwischen Copy- und Direct-Init. Es gibt jedoch einen Unterschied zwischen list-init und no list-init. Das heißt, wir könnten genauso gut vergleichen

int x (a);
int x {a};

Die Listeninitialisierung verbietet in diesem Fall einschränkende Konvertierungen. Eingrenzende Konvertierungen sind in definiert [dcl.init.list]/7 als:

Eine einschränkende Konvertierung ist eine implizite Konvertierung

  • von einem Gleitkommatyp zu einem ganzzahligen Typ oder

  • aus long double zu double oder floatoder von double zu floataußer wenn die Quelle ein konstanter Ausdruck ist und der tatsächliche Wert nach der Konvertierung innerhalb des darstellbaren Wertebereichs liegt (auch wenn er nicht genau dargestellt werden kann), oder

  • von einem Integertyp oder Aufzählungstyp ohne Bereichsbegrenzung zu einem Gleitkommatyp, außer wenn die Quelle ein konstanter Ausdruck ist und der tatsächliche Wert nach der Konvertierung in den Zieltyp passt und den ursprünglichen Wert ergibt, wenn er wieder in den ursprünglichen Typ konvertiert wird, oder

  • von einem ganzzahligen Typ oder einem Aufzählungstyp ohne Bereichseinschränkung zu einem ganzzahligen Typ, der nicht alle Werte des ursprünglichen Typs darstellen kann, es sei denn, die Quelle ist ein konstanter Ausdruck, dessen Wert nach ganzzahligen Heraufstufungen in den Zieltyp passt.

  • Was ist mit dieser Art der Initialisierung, die sowohl Klammern als auch Klammern verwendet: std::random_device{}() ?

    – muuuuh

    24. Januar 2017 um 9:16 Uhr

  • @moooeeeep Dies ist keine separate Art der Initialisierung. Es konstruiert ein temporäres Typ std::random_device indem Sie den Ausdruck verwenden std::random_device{}und ruft dann das überladene Objekt auf operator()so wie std::random_device rd; rd() möchten. Das random_device hat ein operator() der den RNG aufruft und eine (Pseudo-)Zufallszahl zurückgibt, siehe en.cppreference.com/w/cpp/numeric/random/random_device/…

    – dyp

    25. Januar 2017 um 8:33 Uhr

  • Nett, danke! Jetzt wo du es erklärt hast, scheint es offensichtlich.

    – muuuuh

    25. Januar 2017 um 8:36 Uhr

  • Ich habe versucht, mit int b1{2147483648} zu initialisieren; . Aber ich bekomme keinen Fehler, stattdessen bekomme ich nur die Warnung “Warnung: Narrowing Conversion of ‘2147483648ll’ from ‘long long int’ to ‘int’ inside { } [-Wnarrowing]|. Warum ist es so?

    – Rajesh

    9. Oktober 2017 um 18:38 Uhr

  • @Rajesh Welcher Compiler und welche Version? Anscheinend war dies nur bis gcc 5 eine Warnung. Siehe auch: gcc.gnu.org/bugzilla/show_bug.cgi?id=55783

    – dyp

    10. Oktober 2017 um 6:41 Uhr


Während für int die vorhandenen Antworten vollständig sind, I schmerzlich herausgefunden, dass es in einigen Fällen andere Unterschiede zwischen den gibt () und {} Initialisierungen.

Das Stichwort ist das {} ist eine Initialisierungsliste.

Einer dieser Fälle ist die std::string Initialisierung mit count Kopien von A char:

std::string stars(5, '*')

wird initialisiert stars wie *****aber

std::string stars{5, '*'}

wird gelesen als std::string stars(char(5), '*') und initialisiere Stern als * (mit vorangestelltem verstecktem Zeichen).

Die erste ist die Kopierinitialisierung, während die zweite die Listeninitialisierung ist.

Normalerweise wird die Kopierinitialisierung jedoch weniger verwendet. Denn wenn Sie dies tun, indem Sie Objekte benutzerdefinierter Typen übergeben, verursacht dies nur eine Bitkopie und führt daher möglicherweise nicht zu den beabsichtigten Ergebnissen, wenn die benutzerdefinierte Klasse Zeiger verwendet.

  • Sicherlich nicht, wenn sie Kopierkonstruktoren haben? Ich bin jetzt mehr als verwirrt.

    – RichieHH

    14. September 2021 um 21:31 Uhr

  • @RichieHH Wenn der benutzerdefinierte Typ Zeiger hat, sollte man lieber den Kopierkonstruktor zusammen mit Konstruktor und Destruktor schreiben (Regel von 3). Wenn es jedoch keinen Kopierkonstruktor gibt, führt dies zu einer “flachen Kopie” und kann zu hängenden Zeigern führen.

    – Yuwi

    15. September 2021 um 10:09 Uhr

  • Exakt. Benutzerdefinierte Typen, die Zeiger verwenden, sollten Kopier- und Initialisierungskonstruktoren haben. Vielleicht bearbeite deine Antwort. Danke, dass Sie zurückgekommen sind.

    – RichieHH

    16. September 2021 um 5:06 Uhr

1019060cookie-checkInitialisierungsunterschied mit oder ohne geschweiften Klammern in C++

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy