Poza kodem ogólnym (tj. Szablonami) możesz (i ja używamy) wszędzie używać nawiasów klamrowych . Zaletą jest to, że działa wszędzie, na przykład nawet w przypadku inicjalizacji w klasie:
struct foo {
std::string a = { "foo" };
std::string b { "bar" };
std::string c("qux");
std::string d = "baz";
};
lub dla argumentów funkcji:
void foo(std::pair<int, double*>);
foo({ 42, nullptr });
foo(std::pair<int, double*>(42, nullptr));
W przypadku zmiennych nie zwracam zbytniej uwagi między stylami T t = { init };
lub T t { init };
, uważam, że różnica jest niewielka i w najgorszym przypadku spowoduje tylko pomocny komunikat kompilatora o niewłaściwym użyciu explicit
konstruktora.
Dla typów, które akceptują, std::initializer_list
choć oczywiście czasami std::initializer_list
potrzebne są konstruktory (na przykład klasyczny std::vector<int> twenty_answers(20, 42);
). Dobrze jest wtedy nie używać aparatu ortodontycznego.
Jeśli chodzi o kod ogólny (np. W szablonach), ostatni akapit powinien wywołać pewne ostrzeżenia. Rozważ następujące:
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{ return std::unique_ptr<T> { new T { std::forward<Args>(args)... } }; }
Następnie auto p = make_unique<std::vector<T>>(20, T {});
tworzy wektor o rozmiarze 2, jeśli T
jest np. int
, Lub wektor o rozmiarze 20, jeśli T
jest std::string
. Bardzo charakterystycznym znakiem, że dzieje się tutaj coś bardzo złego, jest to, że nie ma cechy, która może cię tutaj uratować (np. Z SFINAE): std::is_constructible
dotyczy inicjalizacji bezpośredniej, podczas gdy używamy inicjalizacji nawiasami klamrowymi, która odracza do inicjalizacja wtedy i tylko wtedy, gdy żaden konstruktor nie std::initializer_list
przeszkadza. Podobnie std::is_convertible
nie pomoże.
Zbadałem, czy rzeczywiście można ręcznie rzucić cechę, która może to naprawić, ale nie jestem co do tego zbyt optymistyczny. W każdym razie nie sądzę, żeby wiele nam brakowało, myślę, że fakt, że make_unique<T>(foo, bar)
wynikiem jest konstrukcja równoważna z, T(foo, bar)
jest bardzo intuicyjny; zwłaszcza biorąc pod uwagę, że make_unique<T>({ foo, bar })
jest zupełnie niepodobne i ma sens tylko jeśli foo
i bar
tego samego typu.
Dlatego w przypadku kodu ogólnego używam tylko nawiasów klamrowych do inicjowania wartości (np. T t {};
Lub T t = {};
), co jest bardzo wygodne i wydaje mi się, że jest lepsze niż sposób C ++ 03 T t = T();
. W przeciwnym razie jest to albo bezpośrednia składnia inicjalizacji (tj. T t(a0, a1, a2);
), Albo czasami konstrukcja domyślna ( T t; stream >> t;
jest to jedyny przypadek, w którym używam tego, co myślę).
Nie oznacza to jednak, że wszystkie nawiasy klamrowe są złe, rozważ poprzedni przykład z poprawkami:
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{ return std::unique_ptr<T> { new T(std::forward<Args>(args)...) }; }
To nadal używa nawiasów klamrowych do konstruowania std::unique_ptr<T>
, mimo że rzeczywisty typ zależy od parametru szablonu T
.