Außerhalb von generischem Code (dh Vorlagen) können Sie (und ich) überall Klammern verwenden . Ein Vorteil ist, dass es überall funktioniert, zum Beispiel auch bei der Initialisierung in der Klasse:
struct foo {
// Ok
std::string a = { "foo" };
// Also ok
std::string b { "bar" };
// Not possible
std::string c("qux");
// For completeness this is possible
std::string d = "baz";
};
oder für Funktionsargumente:
void foo(std::pair<int, double*>);
foo({ 42, nullptr });
// Not possible with parentheses without spelling out the type:
foo(std::pair<int, double*>(42, nullptr));
Bei Variablen, denen ich zwischen den Stilen T t = { init };
oder nicht viel Aufmerksamkeit schenke T t { init };
, ist der Unterschied gering und führt im schlimmsten Fall nur zu einer hilfreichen Compilermeldung über den Missbrauch eines explicit
Konstruktors.
Für Typen, die akzeptieren, std::initializer_list
obwohl offensichtlich manchmal die Nichtkonstruktoren std::initializer_list
benötigt werden (das klassische Beispiel ist std::vector<int> twenty_answers(20, 42);
). Es ist in Ordnung, dann keine Zahnspangen zu verwenden.
Wenn es um generischen Code geht (dh in Vorlagen), sollte dieser allerletzte Absatz einige Warnungen auslösen. Folgendes berücksichtigen:
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{ return std::unique_ptr<T> { new T { std::forward<Args>(args)... } }; }
Dann auto p = make_unique<std::vector<T>>(20, T {});
wird ein Vektor der Größe 2 erstellt, wenn T
z. B. int
ein Vektor der Größe 20, wenn dies der Fall T
ist std::string
. Ein sehr verräterisches Zeichen dafür, dass hier etwas sehr Falsches vor sich geht, ist, dass es keine Eigenschaft gibt, die Sie hier retten kann (z. B. bei SFINAE): std::is_constructible
In Bezug auf die Direktinitialisierung, während wir die Klammerinitialisierung verwenden, die sich von der Direktinitialisierung unterscheidet. Initialisierung genau dann, wenn kein Konstruktor std::initializer_list
eingreift. Ebenso std::is_convertible
hilft nichts.
Ich habe untersucht, ob es tatsächlich möglich ist, ein Merkmal von Hand zu rollen, das dies beheben kann, aber ich bin nicht allzu optimistisch. Auf jeden Fall denke ich nicht, dass wir viel vermissen würden, ich denke, dass die Tatsache, dass make_unique<T>(foo, bar)
eine Konstruktion gleichbedeutend T(foo, bar)
ist, sehr intuitiv ist; vor allem angesichts dessen make_unique<T>({ foo, bar })
ist das ziemlich unähnlich und macht nur Sinn, wenn foo
und bar
haben den gleichen Typ.
Daher verwende ich für generischen Code nur geschweifte Klammern für die Wertinitialisierung (z. B. T t {};
oder T t = {};
), was sehr praktisch ist und meiner Meinung nach der C ++ 03-Methode überlegen ist T t = T();
. Andernfalls handelt es sich entweder um eine direkte Initialisierungssyntax (dh T t(a0, a1, a2);
) oder manchmal um eine Standardkonstruktion (dies T t; stream >> t;
ist der einzige Fall, in dem ich das verwende, was ich denke).
Das bedeutet jedoch nicht, dass alle Klammern schlecht sind. Betrachten Sie das vorherige Beispiel mit Korrekturen:
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{ return std::unique_ptr<T> { new T(std::forward<Args>(args)...) }; }
Dies verwendet immer noch geschweifte Klammern zum Erstellen der std::unique_ptr<T>
, obwohl der tatsächliche Typ vom Vorlagenparameter abhängt T
.