Когда использовать инициализатор, заключенный в скобки?
В C++11 у нас есть новый синтаксис для инициализации классов, который дает нам большое количество возможностей для инициализации переменных.
{ // Example 1
int b(1);
int a{1};
int c = 1;
int d = {1};
}
{ // Example 2
std::complex<double> b(3,4);
std::complex<double> a{3,4};
std::complex<double> c = {3,4};
auto d = std::complex<double>(3,4);
auto e = std::complex<double>{3,4};
}
{ // Example 3
std::string a(3,'x');
std::string b{3,'x'}; // oops
}
{ // Example 4
std::function<int(int,int)> a(std::plus<int>());
std::function<int(int,int)> b{std::plus<int>()};
}
{ // Example 5
std::unique_ptr<int> a(new int(5));
std::unique_ptr<int> b{new int(5)};
}
{ // Example 6
std::locale::global(std::locale("")); // copied from 22.4.8.3
std::locale::global(std::locale{""});
}
{ // Example 7
std::default_random_engine a {}; // Stroustrup's FAQ
std::default_random_engine b;
}
{ // Example 8
duration<long> a = 5; // Stroustrup's FAQ too
duration<long> b(5);
duration<long> c {5};
}
для каждой переменной, которую я объявляю, я должен подумать, какой синтаксис инициализации я должен использовать, и это замедляет мою скорость кодирования. Я уверен, что это не было намерением вводить фигурные скобки.
когда дело доходит до кода шаблона, изменение синтаксиса может привести к различным значениям, поэтому правильный путь существенный.
интересно, есть ли универсальное руководство, какой синтаксис нужно выбрал.
3 ответов:
Я думаю следующее Может быть хорошим руководством:
Если (единственное) значение, которое вы инициализируете, предназначено для точное значение объекта, используйте copy (
=) инициализация (потому что тогда в случае ошибки вы никогда не будете случайно вызывать явный конструктор, который обычно интерпретирует предоставленное значение по-разному). В местах, где инициализация копирования недоступна, см. раздел имеет ли инициализация фигурной скобки правильная семантика, и если это так, используйте это; в противном случае используйте инициализацию скобок (если это также недоступно, вам все равно не повезло).Если значения, которые вы инициализируете, являются списком значений, которые должны быть хранится в объекте (как элементы вектора/массива, или реальная/мнимая части комплексного числа), используйте фигурные скобки инициализации, если таковые имеются.
Если значения инициализации с не значения, которые будут сохранены, но описания Предполагаемое значение / состояние объекта, используйте круглые скобки. Примеры-это аргумент размера a
vectorили аргумент имени файлаfstream.
Я почти уверен, что никогда не будет универсального руководства. Мой подход заключается в использовании всегда фигурных скобок, помня, что
- конструкторы списка инициализаторов имеют приоритет над другими конструкторами
- все стандартные контейнеры библиотеки и std::basic_string имеют конструкторы списка инициализаторов.
- инициализация фигурной скобки не позволяет сужать преобразования.
такие круглые и фигурные скобки не являются взаимозаменяемыми. Но знание того, где они отличаются, позволяет мне использовать инициализацию curly over round bracket в большинстве случаев (некоторые из случаев, когда я не могу в настоящее время ошибки компилятора).
вне общего кода (т. е. шаблонов), вы можете (и я делаю) использовать фигурные скобки везде. Одним из преимуществ является то, что он работает везде, например, даже для инициализации в классе:
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-инициализацию, которая переносится на прямую инициализацию если и только если там нет конструктора принимая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 wayT 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.
Comments