Ответ 1
Код плохо сформирован. Применяется §8.5.4/(3.6):
В противном случае, если
T
- тип класса, рассматриваются конструкторы. применимые конструкторы перечислены и выбран лучший через разрешение перегрузки (13.3, 13.3.1.7).
Теперь, §13.3.3.1.5 идет
Когда аргумент представляет собой список инициализаторов (8.5.4), это не выражение и специальные правила для преобразования это к типу параметра. [...] если тип параметра
std::initializer_list<X>
и все элементы списка инициализаторов могут быть неявно преобразованы вX
, неявная последовательность преобразований является наихудшим преобразованием, необходимым для преобразовать элемент списка вX
, или если в списке инициализаторов нет элементов, преобразование идентичности.
Преобразование 1.1
, которое имеет тип double
(!), int
- это преобразование с плавающим интегралом с рангом конверсии, а преобразование с 1.1
в float
- это преобразование с плавающей точкой - также имеющий ранг конверсии.
Таким образом, оба преобразования одинаково хороши, и поскольку § 13.3.3.2/(3.1) также не могут их отличить, вызов неоднозначен. Обратите внимание, что сужение не играет роли до тех пор, пока не будет выполнено разрешение перегрузки и, следовательно, не может повлиять на набор кандидатов или процесс выбора. Точнее, кандидат должен соответствовать требованию, установленному в 13.3.2/3:
Во-вторых, для
F
, чтобы быть жизнеспособной функцией, должно существовать для каждого аргумент - неявная последовательность преобразования (13.3.3.1), которая преобразует этот аргумент соответствует соответствующему параметруF
.
Однако, как показано во второй цитате, неявная последовательность преобразования, которая преобразует {1.1}
в std::initializer_list<int>
, является наихудшим преобразованием от 1.1
до int
, которое является преобразованием с плавающим интегралом - и действительным ( и существующий!) один на этом.
Если вместо этого вы передадите
{1.1f}
или измените значение initializer_list<float>
на <double>
, код будет корректным, так как преобразование 1.1f
в float
является преобразованием идентичности. Стандарт дает соответствующий пример в (3.6):
[Пример:
struct S { S(std::initializer_list<double>); // #1 S(std::initializer_list<int>); // #2 }; S s1 = { 1.0, 2.0, 3.0 }; // invoke #1
- конец примера]
Еще интереснее,
struct S {
S(std::initializer_list<double>); // #1
S(std::initializer_list<int>); // #2
};
S s1 = { 1.f }; // invoke #1
Также действителен - поскольку преобразование с 1.f
в double
является плавающей точкой promotion, имея рейтинг продвижения, который лучше, чем рейтинг конверсии.