MyClass a1 {a}; // klarer und weniger fehleranfällig als die anderen drei
MyClass a2 = {a};
MyClass a3 = a;
MyClass a4(a);
Warum?
MyClass a1 {a}; // klarer und weniger fehleranfällig als die anderen drei
MyClass a2 = {a};
MyClass a3 = a;
MyClass a4(a);
Warum?
Im Grunde genommen kopiere und füge ich aus Bjarne Stroustrups "The C++ Programming Language 4th Edition" ein:
Listeninitialisierung erlaubt kein Eingrenzen (§iso.8.5.4). Das bedeutet:
Beispiel:
void fun(double val, int val2) {
int x2 = val; // wenn val == 7.9, wird x2 zu 7 (schlecht)
char c2 = val2; // wenn val2 == 1025, wird c2 zu 1 (schlecht)
int x3 {val}; // Fehler: mögliche Abschneidung (gut)
char c3 {val2}; // Fehler: mögliche Eingrenzung (gut)
char c4 {24}; // OK: 24 kann genau als char dargestellt werden (gut)
char c5 {264}; // Fehler (bei 8-Bit-Zeichen angenommen): 264 kann nicht als char dargestellt werden (gut)
int x4 {2.0}; // Fehler: keine Umwandlung vom Double- zum Int-Wert (gut)
}
Die einzige Situation, in der = gegenüber {} bevorzugt wird, ist bei der Verwendung des auto
-Schlüsselworts, um den Typ durch den Initialisierer bestimmen zu lassen.
Beispiel:
auto z1 {99}; // z1 ist ein int
auto z2 = {99}; // z2 ist std::initializer_list
auto z3 = 99; // z3 ist ein int
Vorzugsweise {}-Initialisierung gegenüber Alternativen verwenden, es sei denn, es gibt einen guten Grund dagegen.
Es gibt bereits großartige Antworten zu den Vorteilen der Verwendung der Listeninitialisierung, aber meine persönliche Faustregel lautet, KEINE geschweiften Klammern zu verwenden, wann immer möglich, sondern dies vom konzeptuellen Sinn abhängig zu machen:
Nach meiner Erfahrung kann dieser Regelssatz viel konsistenter angewendet werden als die Verwendung von geschweiften Klammern als Standard, aber man muss sich explizit an alle Ausnahmen erinnern, wenn sie nicht verwendet werden können oder eine andere Bedeutung haben als die "normale" Funktionsaufruf-Syntax mit Klammern (ruft eine andere Überladung auf).
Es passt z. B. gut zu Standardbibliothekstypen wie std::vector
:
vector a{10, 20}; // Geschweifte Klammern -> füllt den Vektor mit den Argumenten
vector b(10, 20); // Klammern -> verwendet Argumente zur Parametrisierung einer Funktionalität,
vector c(it1, it2); // wie das Füllen des Vektors mit 10 Ganzzahlen oder das Kopieren eines Bereichs.
vector d{}; // leere Klammern -> standardmäßiger Konstruktoraufruf für Vektor, der äquivalent ist
// zu einem Vektor, der mit Null-Elementen gefüllt ist
Es gibt VIELE Gründe, die geschweifte Initialisierung zu verwenden, aber Sie sollten sich dessen bewusst sein, dass der initializer_list<>
-Konstruktor den anderen Konstruktoren vorgezogen wird, mit Ausnahme des Standardkonstruktors. Dies führt zu Problemen mit Konstruktoren und Vorlagen, bei denen der Typ T
-Konstruktor entweder eine Initialisierungsliste oder ein einfacher alter Konstruktor sein kann.
struct Foo {
Foo() {}
Foo(std::initializer_list) {
std::cout << "Initialisierungsliste" << std::endl;
}
Foo(const Foo&) {
std::cout << "Kopierkonstruktor" << std::endl;
}
};
int main() {
Foo a;
Foo b(a); // Kopierkonstruktor
Foo c{a}; // Kopierkonstruktor (Initialisierungslisten-Element) + Initialisierungsliste!!!
}
Vorausgesetzt, Sie stoßen nicht auf solche Klassen, gibt es wenig Grund, die Initialisierungsliste nicht zu verwenden.
Aktualisierung (2022-02-11): Bitte beachten Sie, dass es zu dem Thema neuere Meinungen gibt, die gegen die Präferenz des {}-Initialisierers argumentieren, wie zum Beispiel Arthur Dwyer in seinem Blog-Beitrag zu The Knightmare of Initialization in C++.
Ursprüngliche Antwort:
Lesen Sie Herb Sutters (aktualisierten) GotW #1. Dort wird ausführlich der Unterschied zwischen diesen erklärt, sowie einige weitere Optionen, zusammen mit einigen Fallstricken, die relevant sind, um das Verhalten der verschiedenen Optionen zu unterscheiden.
Der Kernpunkt / aus Abschnitt 4 kopiert:
Wann sollten Sie ( ) vs. { } Syntax verwenden, um Objekte zu initialisieren? Warum? Hier ist die einfache Richtlinie:
Richtlinie: Verwenden Sie bevorzugt die Initialisierung mit { }, wie zum Beispiel vector v = { 1, 2, 3, 4 }; oder auto v = vector{ 1, 2, 3, 4 };, da es konsistenter, korrekter ist und vermeidet, alte Fallstricke zu kennen. In Fällen mit einem Argument, in denen Sie nur das =-Zeichen sehen möchten, wie int i = 42; und auto x = anything; ist das Weglassen der Klammern in Ordnung. …
Das deckt die überwiegende Mehrheit der Fälle ab. Es gibt nur eine wichtige Ausnahme:
… In seltenen Fällen, wie vector v(10,20); oder auto v = vector(10,20);, verwenden Sie die Initialisierung mit ( ), um explizit einen Konstruktor aufzurufen, der sonst durch einen initializer_list-Konstruktor versteckt ist.
Dennoch sollte dies im Allgemeinen "selten" sein, da Standard- und Kopierkonstruktion bereits speziell sind und gut mit { } funktionieren, und gutes Klassendesign jetzt den Resort-zu-( )-Fall für benutzerdefinierte Konstruktoren weitgehend vermeidet, aufgrund dieser abschließenden Designrichtlinie:
Richtlinie: Vermeiden Sie es, in einer Klasse einen Konstruktor bereitzustellen, der mehrdeutig mit einem initializer_list-Konstruktor überladen ist, damit Benutzer nicht ( ) verwenden müssen, um einen solchen versteckten Konstruktor zu erreichen.
Beachten Sie auch die Core-Richtlinien zu diesem Thema: ES.23: Bevorzugen Sie die {}-Initialisierungssyntax.
Nur sicherer, solange Sie nicht mit -Wno-narrowing
erstellen, wie zum Beispiel Google es in Chromium macht. Wenn Sie das tun, ist es weniger sicher. Ohne diese Flagge werden die einzigen unsicheren Fälle jedoch durch C++20 behoben.
Hinweis:
Die Kombination dieser beiden bedeutet, dass sie sicherer sind, wenn primitive Konstanten enthalten sind, aber weniger sicher, wenn es sich um Objekte handelt (wird jedoch in C++20 behoben).
CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.