Читать «Эффективный и современный С++. 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}; // Фигурные скобки, второй конструктор