В C++11 появился новый синтаксис инициализатора с фигурными скобками {}
. Он предназначен как для auto
, был высок шанс выразить не то, что вам нужно. В C++17 появился улучшенный набор правил инициализатора. В следующем примере вы увидите, как грамотно инициализировать переменные в С++17 и какой синтаксис при этом использовать.
Как это делается
Переменные инициализируются в один прием. При использовании синтаксиса инициализатора могут возникнуть две разные ситуации.
1. Применение синтаксиса инициализатора с фигурными скобками без выведения типа auto
:
// Три идентичных способа инициализировать переменную типа int:
int x1 = 1;
int x2 {1};
int x3 (1);
std::vector
// Вектор, содержащий три переменные типа int: 1, 2, 3
std::vector
// Такой же вектор
std::vector
// Вектор, содержащий десять переменных типа int,
// каждая из которых имеет значение 20
2. Использование синтаксиса инициализатора с фигурными скобками с выведением типа auto
:
auto v {1}; // v имеет тип int
auto w {1, 2}; // ошибка: при автоматическом выведении типа
// непосредственная инициализация разрешена
// только одиночными элементами! (нововведение)
auto x = {1}; // x имеет тип std::initializer_list
auto y = {1, 2}; // y имеет тип std::initializer_list
auto z = {1, 2, 3.0}; // ошибка: нельзя вывести тип элемента
Как это работает
Отдельно от механизма выведения типа auto
оператор {}
ведет себя предсказуемо, по крайней мере при инициализации обычных типов. При инициализации контейнеров наподобие std::vector
, std::list
и т.д. инициализатор с фигурными скобками будет соответствовать конструктору std::initializer_list
этого класса-контейнера. При этом он не может соответствовать неагрегированным конструкторам (таковыми являются обычные конструкторы, в отличие от тех, что принимают список инициализаторов).
std::vector
, например, предоставляет конкретный неагрегированный конструктор, заносящий в некоторое количество элементов одно и то же значение: std::vector
. При записи std::vector
выбирается конструктор initializer_list
, инициализирующий вектор с двумя элементами: N
и value
. Об этом следует помнить.
Есть интересное различие между оператором {}
и вызовом конструктора с помощью обычных скобок ()
. В первом случае не выполняется неявных преобразований типа: int x (1.2);
и int x = 1.2;
инициализируют переменную x
значением 1
, округлив в нижнюю сторону число с плавающей точкой и преобразовав его к типу int
. А вот выражение int x {1.2};
не скомпилируется, поскольку должно
{}
будет выбран единственный подходящий конструктор, в то время как в момент применения обычных скобок ()
— ближайший похожий конструктор, а также выполнится преобразование типов.
Дополнительное правило, включенное в С++17, касается инициализации с выведением типа auto: несмотря на то что в C++11 тип переменной auto x{123};
(std::initializer_list
с одним элементом) будет определен корректно, скорее всего, это не тот тип, который нужен. В С++17 та же переменная будет типа int
.
Основные правила:
□ в конструкции auto var_name {one_element};
переменная var_name
будет иметь тот же тип, что и one_element;
□ конструкция auto var_name {element1, element2,};
недействительна и не будет скомпилирована;
□ конструкция auto var_name = {element1, element2,};
будет иметь тип std::initializer_list
, где T
— тип всех элементов списка.
В С++17 гораздо сложнее случайно определить список инициализаторов.