Читать «Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14» онлайн - страница 42

Скотт Мейерс

class Widget {

public:

 Widget(int i, bool b);                         // Как ранее

 Widget(int i, double d);                       // Как ранее

 Widget(std::initializer_list<long double> il); // Как ранее

operator float() const; // Преобразование во float

};

Widget w5(w4);            // Использует круглые скобки, вызывает

                          // копирующий конструктор

Widget w6{w4};            // Использует фигурные скобки, вызов

                          // конструктора с std::initializer_list

                          // (w4 преобразуется во float, а float

                          // преобразуется в long double)

Widget w7(std::move(w4)); // Использует круглые скобки, вызывает

                          // перемещающий конструктор

Widget w8{std::move(w4)}; // Использует фигурные скобки, вызов

                          // конструктора с std::initializer_list

                          // (все, как для w6)

Определение компилятором соответствия фигурных инициализаторов конструкторам с std::initializer_list настолько строгое, что доминирует даже тогда, когда конструктор с std::initializer_list с наилучшим соответствием не может быть вызван, например:

class Widget {

public:

 Widget(int i, bool b);   // Как ранее

 Widget(int i, double d); // Как ранее

 Widget(std::initializer_list<bool> il); // Теперь тип

                                         // элемента – bool

}; // Нет функций неявного преобразования

Widget w{10, 5.0}; // Ошибка! Требуется сужающее преобразование

Здесь компилятор игнорирует первые два конструктора (второй из которых в точности соответствует обоим типам аргументов) и пытается вызвать конструктор, получающий аргумент типа std::initializer_list<bool>. Вызов этого конструктора требует преобразования значений int(10) и double(5.0) в bool. Оба эти преобразования являются сужающими (bool не может в точности представить ни первое, ни второе значения), а так как сужающие преобразования запрещены в фигурных инициализаторах, вызов является некорректным, и код отвергается.

И только если нет никакой возможности преобразовать типы аргументов в фигурном инициализаторе в типы в std::initializer_list, компилятор возвращается к нормальному разрешению перегрузки. Например, если мы заменим конструктор с std::initializer_list<bool> конструктором, принимающим std::initializer_list<std::string>, то кандидатами на вызов вновь станут конструкторы, не принимающие std::initializer_list (поскольку нет никакого способа преобразовать int и bool в std::string:

class Widget {

public:

 Widget(int i, bool b);   // Как ранее

 Widget(int i, double d); // Как ранее

 // Теперь тип элементов std::initializer_list – std::string:

 Widget(std::initializer_list<std::string> il);

 // Нет функций неявного преобразования

};

Widget w1(10, true); // Круглые скобки, первый конструктор

Widget w2{10, true}; // Фигурные скобки, первый конструктор

Widget w3(10, 5.0);  // Круглые скобки, второй конструктор

Widget w4{10, 5.0};  // Фигурные скобки, второй конструктор