MyClass a1 {a}; // clearer and less error-prone than the other three
MyClass a2 = {a};
MyClass a3 = a;
MyClass a4(a);
Dlaczego?
Nie mogłem znaleźć odpowiedzi na SO, więc pozwól mi odpowiedzieć na własne pytanie.
MyClass a1 {a}; // clearer and less error-prone than the other three
MyClass a2 = {a};
MyClass a3 = a;
MyClass a4(a);
Dlaczego?
Nie mogłem znaleźć odpowiedzi na SO, więc pozwól mi odpowiedzieć na własne pytanie.
auto
?std::map<std::string, std::vector<std::string>>::const_iterator
chciałbym z tobą porozmawiać .using MyContainer = std::map<std::string, std::vector<std::string>>;
jest jeszcze lepszy (zwłaszcza, że można to szablon!)Odpowiedzi:
Zasadniczo kopiowanie i wklejanie z „The C ++ Programming Language 4th Edition” Bjarne Stroustrupa :
Inicjalizacja listy nie pozwala na zawężenie (§iso.8.5.4). To jest:
Przykład:
Jedynie sytuacja, w której jest korzystniejszy = {} jest przy użyciu
auto
słowa kluczowego, aby uzyskać typ określony przez inicjatora.Przykład:
Wniosek
Preferuj inicjalizację {} zamiast alternatywnych, chyba że masz wyraźny powód, aby tego nie robić.
źródło
()
może zostać przeanalizowane jako deklaracja funkcji. To mylące i niespójne, że można powiedzieć,T t(x,y,z);
ale nieT t()
. A czasami, na pewnox
, nie możesz nawet powiedziećT t(x);
.std::initializer_list
. RedXIII wspomina o tym problemie (i po prostu go usuwa), a ty go całkowicie ignorujesz.A(5,4)
iA{5,4}
może wywoływać zupełnie różne funkcje, i to jest ważna rzecz, o której należy wiedzieć. Może to nawet prowadzić do połączeń, które wydają się nieintuicyjne. Powiedzenie, że powinieneś preferować{}
, doprowadzi do nieporozumień. Ale to nie twoja wina. Osobiście uważam, że jest to wyjątkowo źle przemyślana funkcja.auto var{ 5 }
i będzie on wywnioskowany jakoint
nie więcej niżstd::initializer_list<int>
.Istnieją już świetne odpowiedzi na temat zalet korzystania z inicjalizacji listy, jednak moją osobistą zasadą jest, aby NIE używać nawiasów klamrowych, gdy tylko jest to możliwe, ale uzależnić je od znaczenia pojęciowego:
Po pierwsze, w ten sposób zawsze jestem pewien, że obiekt zostanie zainicjowany niezależnie od tego, czy np. Jest to „prawdziwa” klasa z domyślnym konstruktorem, który i tak zostanie wywołany, czy też ma wbudowany / POD typ. Po drugie - w większości przypadków - jest zgodny z pierwszą regułą, ponieważ domyślnie zainicjowany obiekt często reprezentuje „pusty” obiekt.
Z mojego doświadczenia wynika, że ten zestaw reguł można stosować znacznie bardziej konsekwentnie niż domyślnie stosowanie nawiasów klamrowych, ale trzeba wyraźnie pamiętać wszystkie wyjątki, gdy nie można ich użyć lub mają inne znaczenie niż „normalna” składnia wywołania funkcji z nawiasami (wywołuje inne przeciążenie).
Np. Dobrze pasuje do standardowych typów bibliotek, takich jak
std::vector
:źródło
const int &b{}
<- nie próbuje utworzyć niezainicjowanego odwołania, ale wiąże je z tymczasowym obiektem całkowitoliczbowym. Drugi przykład:struct A { const int &b; A():b{} {} };
<- nie próbuje utworzyć niezainicjowanego odwołania (jak()
by to zrobił), ale wiąże go z tymczasowym obiektem całkowitym, a następnie pozostawia wisi. Nawet GCC-Wall
nie ostrzega o drugim przykładzie.Istnieje wiele powodów, aby inicjalizacji użycie nawiasów, ale należy pamiętać, że konstruktor jest korzystne dla innych konstruktorów , wyjątkiem jest domyślnym-konstruktor. Prowadzi to do problemów z konstruktorami i szablonami, w których konstruktorem typów może być lista inicjalizująca lub zwykły stary ctor.
initializer_list<>
T
Zakładając, że nie spotkasz się z takimi klasami, nie ma powodu, aby nie używać listy intializatora.
źródło
{ ... }
), chyba że chceszinitializer_list
semantyki (cóż, a być może do domyślnego konstruowania obiektu).std::initializer_list
reguła w ogóle istnieje - to tylko wprowadza zamieszanie i bałagan w języku. Co jest złego w robieniu,Foo{{a}}
jeśli chceszstd::initializer_list
konstruktora? Wydaje się to o wiele łatwiejsze do zrozumienia niżstd::initializer_list
mieć pierwszeństwo przed wszystkimi innymi przeciążeniami.Foo{{a}}
kieruje się dla mnie logiką znacznie bardziej niż ta,Foo{a}
która zmienia się w pierwszeństwo listy inicjalizatorów (ups może pomyśleć hm ...)std::initializer_list<Foo>
konstruktora, ale w pewnym momencie zostanie on dodany doFoo
klasy w celu rozszerzenia interfejsu? Następnie użytkownicyFoo
klasy są popieprzeni.initializer_list<>
), który tak naprawdę nie kwalifikuje, kto twierdzi, że jest preferowany, a następnie wspomina o jednym dobrym przypadku, w którym NIE jest preferowany. Czego mi brakuje, że ok. 30 innych osób (stan na 21.04.2016) było pomocnych?Jest to bezpieczniejsze tylko pod warunkiem, że nie budujesz z -nie zawężaniem, jak powiedzmy, że Google robi w Chromium. Jeśli tak, to MNIEJ bezpieczniej. Bez tej flagi jedyne niebezpieczne przypadki zostaną naprawione przez C ++ 20.
Uwaga: A) Nawiasy klamrowe są bezpieczniejsze, ponieważ nie pozwalają na zwężenie. B) Nawiasy klamrowe są mniej bezpieczne, ponieważ mogą ominąć konstruktory prywatne lub usunięte i wywołać jawnie oznaczone konstruktory.
Te dwie kombinacje oznaczają, że są bezpieczniejsze, jeśli to, co jest w środku, jest prymitywnymi stałymi, ale mniej bezpieczne, jeśli są obiektami (chociaż ustalone w C ++ 20)
źródło