Читать «Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14» онлайн - страница 41
Скотт Мейерс
Основной недостаток фигурной инициализации — временами сопровождающее ее удивительное поведение. Такое поведение вырастает из необыкновенно запутанных взаимоотношений между фигурной инициализацией, std::initializer_list
и разрешением перегрузки конструкторов. Их взаимодействие может привести к коду, который, как кажется, должен делать что-то одно, а в результате делает что-то совсем другое. Например, в разделе 1.2 поясняется, что когда переменная, объявленная как auto
, имеет инициализатор в фигурных скобках, то выводимым типом является std::initializer_list
, несмотря на то что другой способ объявления переменной с тем же инициализатором даст более ожидаемый тип. В результате чем больше вам нравится auto
, тем меньше энтузиазма вы должны проявить по отношению к фигурной инициализации.
В вызовах конструктора круглые и фигурные скобки имеют один и тот же смысл, пока в конструкторах не принимают участие параметры std::initializer_list
:
class Widget {
public:
Widget(int i, bool b); // Конструкторы не имеют параметров
Widget(int i, double d); // std::initializer_list
};
Widget w1(10, true); // Вызов первого конструктора
Widget w2{10, true}; // Вызов первого конструктора
Widget w3(10, 5.0); // Вызов второго конструктора
Widget w4{10, 5.0}; // Вызов второго конструктора
Если же один или несколько конструкторов объявляют параметр типа std::initializer_list
, вызовы, использующие синтаксис фигурной инициализации, строго предпочитают перегрузки, принимающие std::initializer_list
. Строго. Если у компилятора есть std::initializer_ list
, он использует именно это толкование. Если класс Widget
выше дополнить конструктором, принимающим, например, std::initializer_list<long double>
class Widget {
public:
Widget(int i, bool b); // Как и ранее
Widget(int i, double d); // Как и ранее
Widget(std::initializer_list<long double> il); // Добавлен
…
};
то w2
и w4
будут созданы с помощью нового конструктора, несмотря на то что тип элементов std::initializer_list
(в данном случае — long double
) хуже соответствует обоим аргументам по сравнению с конструкторами, не принимающими std::initializer_list
! Смотрите сами:
Widget w1(10, true); // Использует круглые скобки и, как и
// ранее, вызывает первый конструктор
Widget w2{10, true}; // Использует фигурные скобки, но теперь
// вызывает третий конструктор
// (10 и true преобразуются в long double)
Widget w3(10, 5.0); // Использует круглые скобки и, как и
// ранее, вызывает второй конструктор
Widget w4{10, 5.0}; // Использует фигурные скобки, но теперь
// вызывает третий конструктор
// (10 и 5.0 преобразуются в long double)
Даже то, что в обычной ситуации представляет собой копирующий или перемещающий конструктор, может быть перехвачено конструктором с std::initializer_list
: