Когда использовать инициализатор, заключенный в скобки?



В 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};
}


для каждой переменной, которую я объявляю, я должен подумать, какой синтаксис инициализации я должен использовать, и это замедляет мою скорость кодирования. Я уверен, что это не было намерением вводить фигурные скобки.



когда дело доходит до кода шаблона, изменение синтаксиса может привести к различным значениям, поэтому правильный путь существенный.



интересно, есть ли универсальное руководство, какой синтаксис нужно выбрал.

692   3  

3 ответов:

Я думаю следующее Может быть хорошим руководством:

  • Если (единственное) значение, которое вы инициализируете, предназначено для точное значение объекта, используйте copy (=) инициализация (потому что тогда в случае ошибки вы никогда не будете случайно вызывать явный конструктор, который обычно интерпретирует предоставленное значение по-разному). В местах, где инициализация копирования недоступна, см. раздел имеет ли инициализация фигурной скобки правильная семантика, и если это так, используйте это; в противном случае используйте инициализацию скобок (если это также недоступно, вам все равно не повезло).

  • Если значения, которые вы инициализируете, являются списком значений, которые должны быть хранится в объекте (как элементы вектора/массива, или реальная/мнимая части комплексного числа), используйте фигурные скобки инициализации, если таковые имеются.

  • Если значения инициализации с не значения, которые будут сохранены, но описания Предполагаемое значение / состояние объекта, используйте круглые скобки. Примеры-это аргумент размера a vector или аргумент имени файла fstream.

Я почти уверен, что никогда не будет универсального руководства. Мой подход заключается в использовании всегда фигурных скобок, помня, что

  1. конструкторы списка инициализаторов имеют приоритет над другими конструкторами
  2. все стандартные контейнеры библиотеки и std::basic_string имеют конструкторы списка инициализаторов.
  3. инициализация фигурной скобки не позволяет сужать преобразования.

такие круглые и фигурные скобки не являются взаимозаменяемыми. Но знание того, где они отличаются, позволяет мне использовать инициализацию 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 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.

Comments

    Ничего не найдено.