MyClass a1 {a}; // clearer and less error-prone than the other three
MyClass a2 = {a};
MyClass a3 = a;
MyClass a4(a);
Wieso den?
Oleksiy
MyClass a1 {a}; // clearer and less error-prone than the other three
MyClass a2 = {a};
MyClass a3 = a;
MyClass a4(a);
Wieso den?
Oleksiy
Grundsätzlich Kopieren und Einfügen von Bjarne Stroustrup “Die Programmiersprache C++ 4. Auflage”:
Listeninitialisierung erlaubt keine Einengung (§iso.8.5.4). Das ist:
Beispiel:
void fun(double val, int val2) {
int x2 = val; // if val == 7.9, x2 becomes 7 (bad)
char c2 = val2; // if val2 == 1025, c2 becomes 1 (bad)
int x3 {val}; // error: possible truncation (good)
char c3 {val2}; // error: possible narrowing (good)
char c4 {24}; // OK: 24 can be represented exactly as a char (good)
char c5 {264}; // error (assuming 8-bit chars): 264 cannot be
// represented as a char (good)
int x4 {2.0}; // error: no double to int value conversion (good)
}
Das nur Situation, in der = gegenüber {} bevorzugt wird, wenn verwendet wird auto
Schlüsselwort, um den vom Initialisierer bestimmten Typ abzurufen.
Beispiel:
auto z1 {99}; // z1 is an int
auto z2 = {99}; // z2 is std::initializer_list<int>
auto z3 = 99; // z3 is an int
Bevorzugen Sie die {}-Initialisierung gegenüber Alternativen, es sei denn, Sie haben einen triftigen Grund, dies nicht zu tun.
Hinzu kommt die Tatsache, dass mit ()
kann als Funktionsdeklaration geparst werden. Es ist verwirrend und widersprüchlich, was Sie sagen können T t(x,y,z);
aber nicht T t()
. Und manchmal bist du dir sicher x
kann man gar nicht sagen T t(x);
.
– Juanchopanza
14. August 2013 um 5:23 Uhr
Ich bin mit dieser Antwort absolut nicht einverstanden; Die geklammerte Initialisierung wird zu einem kompletten Durcheinander, wenn Sie Typen mit einem ctor haben, der a akzeptiert std::initializer_list
. RedXIII erwähnt dieses Problem (und wischt es einfach weg), während Sie es vollständig ignorieren. A(5,4)
und A{5,4}
kann ganz andere Funktionen aufrufen, und das ist wichtig zu wissen. Es kann sogar zu Anrufen führen, die nicht intuitiv erscheinen. Zu sagen, dass Sie es vorziehen sollten {}
standardmäßig dazu führen, dass die Leute missverstehen, was vor sich geht. Das ist aber nicht deine Schuld. Ich persönlich halte das für ein extrem schlecht durchdachtes Feature.
– Benutzer1520427
2. Februar 2015 um 1:40 Uhr
@user1520427 Deshalb gibt es den “es sei denn, Sie haben einen triftigen Grund, dies nicht zu tun” Teil.
– Oleksi
2. Februar 2015 um 16:54 Uhr
Obwohl diese Frage alt ist, hat sie einige Treffer, daher füge ich sie hier nur als Referenz hinzu (ich habe sie nirgendwo anders auf der Seite gesehen). Ab C++14 mit dem neuen Regeln für den automatischen Abzug von geklammerten Init-Listen es ist jetzt möglich zu schreiben auto var{ 5 }
und es wird abgeleitet als int
nicht mehr als std::initializer_list<int>
.
– Edoardo Dominici
31. Oktober 2015 um 12:26 Uhr
Haha, aus all den Kommentaren ist immer noch nicht klar, was zu tun ist. Klar ist, dass die C++-Spezifikation ein Durcheinander ist!
– Trommel
12. März 2019 um 13:19 Uhr
MikeMB
Es gibt bereits tolle Antworten zu den Vorteilen der Listeninitialisierung, aber meine persönliche Faustregel ist, möglichst keine geschweiften Klammern zu verwenden, sondern es von der konzeptionellen Bedeutung abhängig zu machen:
Meiner Erfahrung nach kann dieser Regelsatz viel konsistenter angewendet werden, als standardmäßig geschweifte Klammern zu verwenden, sich aber alle Ausnahmen explizit merken zu müssen, wenn sie nicht verwendet werden können oder eine andere Bedeutung haben als die “normale” Funktionsaufrufsyntax mit Klammern (ruft eine andere Überladung auf).
Es passt zB gut zu Standard-Bibliothekstypen wie std::vector
:
vector<int> a{10, 20}; //Curly braces -> fills the vector with the arguments
vector<int> b(10, 20); //Parentheses -> uses arguments to parametrize some functionality,
vector<int> c(it1, it2); //like filling the vector with 10 integers or copying a range.
vector<int> d{}; //empty braces -> default constructs vector, which is equivalent
//to a vector that is filled with zero elements
Stimme den meisten Ihrer Antworten voll und ganz zu. Denken Sie jedoch nicht, dass es einfach überflüssig ist, leere Klammern für Vektoren zu setzen? Ich meine, es ist in Ordnung, wenn Sie ein Objekt des generischen Typs T initialisieren müssen, aber was ist der Zweck, dies für nicht generischen Code zu tun?
– Michail
27. November 2016 um 11:15 Uhr
@Mikhail: Es ist sicherlich überflüssig, aber es ist eine Angewohnheit von mir, die Initialisierung lokaler Variablen immer explizit zu machen. Wie ich geschrieben habe, geht es hier hauptsächlich um Konsistenz, also vergesse ich es nicht, wenn es darauf ankommt. Es ist jedoch sicherlich nichts, was ich in einem Code-Review erwähnen oder in einen Styleguide aufnehmen würde.
– MikeMB
27. November 2016 um 16:10 Uhr
ziemlich sauberer Regelsatz.
– See9m
20. März 2018 um 5:57 Uhr
Dies ist bei weitem die beste Antwort. {} ist wie Vererbung – leicht zu missbrauchen, was zu schwer verständlichem Code führt.
– UKMonkey
9. Mai 2018 um 13:36 Uhr
@MikeMB Beispiel: const int &b{}
<- versucht nicht, eine nicht initialisierte Referenz zu erstellen, sondern bindet sie an ein temporäres Integer-Objekt. Zweites Beispiel: struct A { const int &b; A():b{} {} };
<- versucht nicht, eine nicht initialisierte Referenz (wie ()
tun würde), aber binden Sie es an ein temporäres Integer-Objekt und lassen Sie es dann hängen. GCC sogar mit -Wall
warnt nicht vor dem zweiten Beispiel.
– Johannes Schaub – litb
18. November 2018 um 18:05 Uhr
Rot XIII
Es gibt VIELE Gründe, die Klammerinitialisierung zu verwenden, aber Sie sollten sich dessen bewusst sein das initializer_list<>
Konstruktor wird den anderen Konstruktoren vorgezogen, wobei die Ausnahme der Standardkonstruktor ist. Dies führt zu Problemen mit Konstruktoren und Vorlagen, bei denen der Typ T
Konstruktor kann entweder eine Initialisierungsliste oder ein einfacher alter Ctor sein.
struct Foo {
Foo() {}
Foo(std::initializer_list<Foo>) {
std::cout << "initializer list" << std::endl;
}
Foo(const Foo&) {
std::cout << "copy ctor" << std::endl;
}
};
int main() {
Foo a;
Foo b(a); // copy ctor
Foo c{a}; // copy ctor (init. list element) + initializer list!!!
}
Angenommen, Sie begegnen solchen Klassen nicht, gibt es wenig Grund, die Initialisiererliste nicht zu verwenden.
Das ist ein sehr wichtiger Punkt in der generischen Programmierung. Wenn Sie Vorlagen schreiben, nicht Verwenden Sie geklammerte Init-Listen (der Name des Standards für { ... }
), es sei denn, Sie möchten die initializer_list
Semantik (na ja, und vielleicht für die Standardkonstruktion eines Objekts).
– Xeo
14. August 2013 um 7:11 Uhr
Ich verstehe ehrlich gesagt nicht, warum die std::initializer_list
Es gibt sogar eine Regel – sie fügt der Sprache nur Verwirrung und Chaos hinzu. Was ist falsch daran zu tun Foo{{a}}
wenn du willst std::initializer_list
Konstrukteur? Das scheint so viel einfacher zu verstehen als zu haben std::initializer_list
haben Vorrang vor allen anderen Überladungen.
– Benutzer1520427
2. Februar 2015 um 1:48 Uhr
+1 für den obigen Kommentar, weil es meiner Meinung nach ein wirkliches Durcheinander ist !! es ist nicht logisch; Foo{{a}}
Einer Logik folgt für mich weit mehr als Foo{a}
was zur Priorität der Initialisierungsliste wird (Ups, könnte der Benutzer denken, hm …)
– Gabriel
19. April 2015 um 19:21 Uhr
Grundsätzlich ersetzt C++11 ein Durcheinander durch ein anderes Durcheinander. Oh, tut mir leid, es ersetzt es nicht – es fügt es hinzu. Wie können Sie wissen, ob Sie solchen Klassen nicht begegnen? Was ist, wenn Sie anfangen ohne std::initializer_list<Foo>
Konstruktor, aber es wird sein hinzugefügt zum Foo
Klasse irgendwann ihre Schnittstelle erweitern? Dann Benutzer von Foo
Klasse sind durcheinander.
– mip
29. Mai 2015 um 9:02 Uhr
.. was sind die “VIELEN Gründe, Klammerinitialisierung zu verwenden”? Diese Antwort weist auf einen Grund hin (initializer_list<>
), was es nicht wirklich qualifiziert wer sagt, dass es bevorzugt wird, und fährt dann fort, einen guten Fall zu erwähnen, wo es ist NICHT bevorzugt. Was vermisse ich, das ~30 andere Personen (Stand: 21.04.2016) hilfreich fanden?
– Dwanderson
21. April 2016 um 14:19 Uhr
Allan Jensen
Es ist nur sicherer, solange Sie nicht mit -Wno-narrowing bauen, wie es beispielsweise Google in Chromium tut. Wenn Sie dies tun, ist es WENIGER sicher. Ohne dieses Flag werden die einzigen unsicheren Fälle jedoch von C++20 behoben.
Hinweis: A) Geschweifte Klammern sind sicherer, da sie keine Verengung zulassen. B) Geschweifte Klammern sind weniger sicher, da sie private oder gelöschte Konstruktoren umgehen und explizit markierte Konstruktoren implizit aufrufen können.
Diese beiden Kombinationen bedeuten, dass sie sicherer sind, wenn es sich bei dem Inhalt um primitive Konstanten handelt, aber weniger sicher, wenn es sich um Objekte handelt (obwohl in C++20 behoben).
Kodierung
Update (11.02.2022): Beachten Sie, dass es zu diesem Thema neuere Meinungen als die ursprünglich gepostete (unten) gibt, die gegen die Bevorzugung des {}-Initialisierers argumentieren, wie Arthur Dwyer in seinem Blogbeitrag on Der Knightmare der Initialisierung in C++.
Ursprüngliche Antwort:
Lesen Herb Sutters (aktualisiert) GotW #1. Dies erklärt im Detail den Unterschied zwischen diesen und einigen weiteren Optionen, zusammen mit einigen Fallstricken, die für die Unterscheidung des Verhaltens der verschiedenen Optionen relevant sind.
Das Wesentliche / kopiert aus Abschnitt 4:
Wann sollten Sie ( ) vs. { } Syntax verwenden, um Objekte zu initialisieren? Wieso den? Hier ist die einfache Richtlinie:
Richtlinie: Bevorzugen Sie die Initialisierung mit { }, z. B. Vektor v = { 1, 2, 3, 4 }; oder auto v = vector{ 1, 2, 3, 4 };, weil es konsistenter und korrekter ist und es vermeidet, sich überhaupt mit Fallstricken im alten Stil auskennen zu müssen. In Fällen mit einem Argument, in denen Sie lieber nur das =-Zeichen sehen möchten, wie z. B. int i = 42; und auto x = irgendetwas; das Weglassen der Klammern ist in Ordnung. …
Damit sind die meisten Fälle abgedeckt. Es gibt nur eine Hauptausnahme:
… In seltenen Fällen wie Vektor v(10,20); oder auto v = vector(10,20);, verwenden Sie die Initialisierung mit ( ), um explizit einen Konstruktor aufzurufen, der andernfalls durch einen initializer_list-Konstruktor verborgen wird.
Der Grund, warum dies jedoch im Allgemeinen „selten“ sein sollte, liegt darin, dass Standard- und Kopierkonstruktion bereits etwas Besonderes sind und gut mit { } funktionieren, und gutes Klassendesign vermeidet aus diesem Grund jetzt meistens den Rückgriff auf ()-Fall für benutzerdefinierte Konstruktoren endgültige Designrichtlinie:
Richtlinie: Vermeiden Sie beim Entwerfen einer Klasse die Bereitstellung eines Konstruktors, der mehrdeutig mit einem initializer_list-Konstruktor überladen wird, damit Benutzer nicht ( ) verwenden müssen, um einen solchen versteckten Konstruktor zu erreichen.
Siehe auch die Kernrichtlinien zu diesem Thema: ES.23: Bevorzugen Sie die {}-Initialisierer-Syntax.
Würde nicht ins Kommentarfeld passen ;). Wie auch immer, um aus dem verlinkten Artikel zu zitieren: “… die Hauptgründe für die Deklaration von Variablen mit auto sind Korrektheit, Leistung, Wartbarkeit und Robustheit – und, ja, Bequemlichkeit …”.
– Markus García
14. August 2013 um 4:01 Uhr
Das stimmt, es ist praktisch, aber es verringert meiner Meinung nach die Lesbarkeit – ich mag es sehen welcher Typ ein Objekt beim Lesen von Code ist. Wenn Sie sich zu 100 % sicher sind, um welchen Objekttyp es sich handelt, warum sollten Sie dann auto verwenden? Und wenn Sie die Listeninitialisierung verwenden (lesen Sie meine Antwort), können Sie sicher sein, dass sie immer korrekt ist.
– Oleksi
14. August 2013 um 4:04 Uhr
@Oleksij:
std::map<std::string, std::vector<std::string>>::const_iterator
würde gerne mit dir sprechen.– Xeo
14. August 2013 um 6:03 Uhr
@Oleksiy Ich empfehle zu lesen dieses GotW.
– Raptz
14. August 2013 um 6:05 Uhr
@doc würde ich sagen
using MyContainer = std::map<std::string, std::vector<std::string>>;
ist sogar noch besser (vor allem, da Sie es schablonen können!)– JAB
18. Februar 2016 um 22:26 Uhr