Используйте нотацию `T{e}` для конструирования объектов
Причина
Синтаксис конструирования T{e} явно указывает, что выполняется конструирование. Синтаксис T{e} не допускает сужающих преобразований. T{e} — единственное безопасное и универсальное выражение для конструирования значения типа T из выражения e. Нотации приведения T(e) и (T)e не являются ни безопасными, ни универсальными.
Пример
Для встроенных типов нотация конструирования защищает от сужения и переинтерпретации:
void use(char ch, int i, double d, char* p, long long lng)
{
int x1 = int{ch}; // OK, но избыточно
int x2 = int{d}; // ошибка: сужение double->int; используйте приведение при необходимости
int x3 = int{p}; // ошибка: указатель->int; используйте reinterpret_cast при необходимости
int x4 = int{lng}; // ошибка: сужение long long->int; используйте приведение при необходимости
int y1 = int(ch); // OK, но избыточно
int y2 = int(d); // плохо: сужение double->int; используйте приведение при необходимости
int y3 = int(p); // плохо: указатель->int; используйте reinterpret_cast при необходимости
int y4 = int(lng); // плохо: сужение long long->int; используйте приведение при необходимости
int z1 = (int)ch; // OK, но избыточно
int z2 = (int)d; // плохо: сужение double->int; используйте приведение при необходимости
int z3 = (int)p; // плохо: указатель->int; используйте reinterpret_cast при необходимости
int z4 = (int)lng; // плохо: сужение long long->int; используйте приведение при необходимости
}
Преобразования целых чисел в указатели и обратно при использовании нотаций T(e) или (T)e определяются реализацией и непереносимы между платформами с разными размерами целых чисел и указателей.
Примечание
Избегайте приведений (явного преобразования типов), а если необходимо, предпочитайте именованные приведения.
Примечание
Когда тип однозначен, T в T{e} можно опустить.
complex<double> f(complex<double>);
auto z = f({2*pi, 1});
Примечание
Нотация конструирования — наиболее общая нотация инициализатора.
Исключение
std::vector и другие контейнеры были определены до того, как появилась нотация {} для конструирования. Рассмотрим:
vector<string> vs {10}; // десять пустых строк
vector<int> vi1 {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // десять элементов 1..10
vector<int> vi2 {10}; // один элемент со значением 10
Как создать vector из 10 инициализированных по умолчанию int?
vector<int> v3(10); // десять элементов со значением 0
Использование () вместо {} для числа элементов является устоявшимся соглашением (восходит к началу 1980-х), трудно изменяемым, но всё же ошибкой проектирования: для контейнера, где тип элемента можно перепутать с числом элементов, возникает неоднозначность, которую необходимо разрешать. Принятое соглашение: {10} — список из одного элемента, (10) — размер.
Эту ошибку не нужно повторять в новом коде. Можно определить тип для представления числа элементов:
struct Count { int n; };
template<typename T>
class Vector {
public:
Vector(Count n); // n элементов, инициализированных по умолчанию
Vector(initializer_list<T> init); // init.size() элементов
// ...
};
Vector<int> v1{10};
Vector<int> v2{Count{10}};
Vector<Count> v3{Count{10}}; // да, здесь всё ещё остаётся небольшая проблема
Основная оставшаяся задача — найти подходящее имя для Count.
Контроль
Отмечать приведения в стиле C (T)e и в функциональном стиле T(e).