Предпочитайте синтаксис инициализации `{}`
Причина
Предпочитайте {}. Правила для инициализации {} проще, универсальнее, менее неоднозначны и безопаснее, чем для других форм инициализации.
Используйте = только когда вы уверены, что сужающих преобразований быть не может. Для встроенных арифметических типов используйте = только вместе с auto.
Избегайте инициализации (), которая допускает синтаксические неоднозначности.
Пример
int x {f(99)};
int y = x;
vector<int> v = {1, 2, 3, 4, 5, 6};
Исключение
Для контейнеров есть традиция использовать {...} для списка элементов и (...) для размеров:
vector<int> v1(10); // вектор из 10 элементов со значением по умолчанию 0
vector<int> v2{10}; // вектор из 1 элемента со значением 10
vector<int> v3(1, 2); // вектор из 1 элемента со значением 2
vector<int> v4{1, 2}; // вектор из 2 элементов со значениями 1 и 2
Примечание
Инициализаторы {} не допускают сужающих преобразований (что, как правило, хорошо) и допускают явные конструкторы (что нормально — мы намеренно инициализируем новую переменную).
Пример
int x {7.9}; // ошибка: сужение
int y = 7.9; // OK: y становится 7. Надеемся на предупреждение компилятора
int z {gsl::narrow_cast<int>(7.9)}; // OK: вы сами этого хотели
auto zz = gsl::narrow_cast<int>(7.9); // OK: вы сами этого хотели
Примечание
Инициализацию {} можно использовать почти для любой инициализации; другие формы инициализации — нет:
auto p = new vector<int> {1, 2, 3, 4, 5}; // инициализированный вектор
D::D(int a, int b) :m{a, b} { // инициализатор члена (например, m может быть парой)
// ...
};
X var {}; // инициализировать var как пустой
struct S {
int m {7}; // инициализатор по умолчанию для члена
// ...
};
По этой причине инициализация {} часто называется «унифицированной инициализацией» (хотя, к сожалению, осталось несколько нерегулярностей).
Примечание
Инициализация переменной, объявленной с auto, единственным значением, например {v}, давала неожиданные результаты до C++17. Правила C++17 несколько менее удивительны:
auto x1 {7}; // x1 является int со значением 7
auto x2 = {7}; // x2 является initializer_list<int> с элементом 7
auto x11 {7, 8}; // ошибка: два инициализатора
auto x22 = {7, 8}; // x22 является initializer_list<int> с элементами 7 и 8
Используйте ={...}, если вам действительно нужен initializer_list<T>:
auto fib10 = {1, 1, 2, 3, 5, 8, 13, 21, 34, 55}; // fib10 является списком
Примечание
={} даёт инициализацию копированием, тогда как {} даёт прямую инициализацию. Как и само различие между инициализацией копированием и прямой инициализацией, это может приводить к неожиданностям. {} принимает explicit конструкторы; ={} — нет. Например:
struct Z { explicit Z() {} };
Z z1{}; // OK: прямая инициализация, поэтому используем явный конструктор
Z z2 = {}; // ошибка: инициализация копированием, поэтому нельзя использовать явный конструктор
Используйте обычную инициализацию {}, если только вы специально не хотите запретить явные конструкторы.
Пример
template<typename T>
void f()
{
T x1(1); // T инициализирован значением 1
T x0(); // плохо: объявление функции (часто ошибка)
T y1 {1}; // T инициализирован значением 1
T y0 {}; // T инициализирован по умолчанию
// ...
}
Смотрите также: Обсуждение
Контроль
- Отмечайте использование
=для инициализации арифметических типов, когда происходит сужение. - Отмечайте использование синтаксиса инициализации
(), которые фактически являются объявлениями. (Многие компиляторы уже должны предупреждать об этом.)