За пределами общего кода (то есть шаблонов) вы можете (и я это делаю) использовать фигурные скобки везде .Одним из преимуществ является то, что он работает везде, например, даже для инициализации в классе:
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";
};
или для аргументов функции:
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));
Для переменных я не обращаю большого внимания междув стилях T t = { init };
или T t { init };
я считаю, что разница незначительна и в худшем случае приведет только к полезному сообщению компилятора о неправильном использовании конструктора explicit
.
Для типов, принимающих std::initializer_list
очевидно, иногда необходимы не std::initializer_list
конструкторы (классический пример std::vector<int> twenty_answers(20, 42);
).Тогда не стоит использовать фигурные скобки.
Когда речь идет об общем коде (то есть в шаблонах), последний абзац должен был вызвать некоторые предупреждения.Рассмотрим следующее:
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{ return std::unique_ptr<T> { new T { std::forward<Args>(args)... } }; }
Затем auto p = make_unique<std::vector<T>>(20, T {});
создает вектор размера 2, если T
, например, int
, или вектор размера 20, если T
равен std::string
.Очень явный признак того, что здесь происходит что-то очень неправильное, заключается в том, что здесь есть черта , а не , которая может спасти вас (например, с помощью SFINAE): std::is_constructible
в терминах прямой инициализации, тогда как мыиспользование brace-initialization, которое отсылает к прямой инициализации тогда и только тогда, когда не имеет конструктора, принимающего std::initializer_list
вмешивающихся.Точно так же std::is_convertible
не поможет.
Я исследовал, возможно ли на самом деле прокрутить черту, которая может это исправить, но я не слишком оптимистичен по этому поводу.В любом случае я не думаю, что мы бы сильно упустили, я думаю, что тот факт, что make_unique<T>(foo, bar)
приводит к конструкции, эквивалентной T(foo, bar)
, очень интуитивен;особенно учитывая, что make_unique<T>({ foo, bar })
совершенно не похож и имеет смысл, если foo
и bar
имеют одинаковый тип.
Следовательно, для универсального кода, я использую только фигурные скобки для инициализации значения (например, T t {};
или T t = {};
), что очень удобно, и я думаю, что лучше, чем C ++ 03 way T t = T();
. В противном случае это либо синтаксис прямой инициализации (то есть T t(a0, a1, a2);
), либо иногда конструкция по умолчанию (T t; stream >> t;
- единственный случай, где я использую то, что мне кажется).
Это не значитчто все фигурные скобки плохие, однако рассмотрим предыдущий пример с исправлениями:
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{ return std::unique_ptr<T> { new T(std::forward<Args>(args)...) }; }
Это все еще использует фигурные скобки для построения std::unique_ptr<T>
, даже если фактический тип зависит от параметра шаблона T
.