Читать «Эффективный и современный С++. 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: