Возник вопрос, когда я изучал ответ на этот вопрос SO. Рассмотрим следующий код:
struct A{
operator char() const{ return 'a'; }
operator int() const{ return 10; }
};
struct B {
void operator<< (int) { }
};
int main()
{
A a;
B b;
b << a;
}
Преобразование a
в int
может быть либо через a.operator char()
, за которым следует интегральная рассылка, либо a.operator int()
, за которой следует преобразование идентичности (т.е. никакого преобразования вообще). В стандарте говорится, что (§13.3.3.1 [over.best.ics]/p10, сноска опущена, смелый мой, все цитаты из N3936):
Если существует несколько различных последовательностей преобразований, каждая конверсия аргумент типа параметра, неявная последовательность преобразования связанный с параметром, определяется как уникальное преобразование последовательность обозначала неоднозначную последовательность преобразований. Для цель ранжирования неявных последовательностей преобразования, как описано в 13.3.3.2, неоднозначная последовательность преобразования рассматривается как определяемая пользователем последовательность, которая неотличима от любых других определяемая пользователем последовательность преобразований. Если функция, использующая неоднозначная последовательность преобразований выбрана в качестве наилучшей жизнеспособной функции, вызов будет плохо сформирован, потому что преобразование одного из аргументы в вызове неоднозначны.
Здесь B::operator<<(int)
является единственным жизнеспособным кандидатом - и, следовательно, является наилучшим жизнеспособным кандидатом, хотя последовательность преобразования для параметра является неоднозначной последовательностью преобразования. Согласно смещенному предложению, вызов должен быть плохо сформирован, потому что "преобразование одного из аргументов в вызове неоднозначно".
Однако ни один компилятор, который я тестировал (g++, clang и MSVC), фактически не сообщает об ошибке, что имеет смысл, поскольку после выбора функции для вызова с помощью разрешения перегрузки функция "параметр (8.3.5) должна быть инициализирована ( 8.5, 12.8, 12.1) с соответствующим аргумент "(§5.2.2 [expr.call]/p4). Эта инициализация - это копирование-инициализация (§8.5 [dcl.init]/p15), и согласно §8.5 [dcl.init]/p17, получается новый раунд разрешения перегрузки, чтобы определить функцию преобразования:
Семантика инициализаторов такова. Тип назначения тип инициализации объекта или ссылки, и Тип источника - тип выражения инициализатора. Если инициализатор не является одним (возможно, в скобках) выражением, тип источника не определен.
- [...]
- Если тип назначения является (возможно, cv-квалифицированным) типом класса: [...]
- В противном случае, если тип источника является (возможно, cv-qualit) классом, рассматриваются функции преобразования. Применимое преобразование функции перечислены (13.3.1.5), и выбран лучший через разрешение перегрузки (13.3). Пользовательское преобразование selected вызывается для преобразования выражения инициализатора в объект инициализируется. Если преобразование не может быть выполнено или неоднозначный, инициализация плохо сформирована.
- [...]
И в этом раунде разрешения перегрузки в разделе 13.3.3 имеется тай-брейк [over.match.best]/p1:
жизнеспособная функция
F1
определяется как лучшая функция, чем другая жизнеспособная функция F2, если для всех аргументовi
,ICSi(F1)
не хуже чемICSi(F2)
, а затем
- для некоторого аргумента
j
,ICSj(F1)
- лучшая последовательность преобразования, чемICSj(F2)
, или, если не это,- контекст представляет собой инициализацию путем пользовательского преобразования (см. 8.5, 13.3.1.5 и 13.3.1.6) и стандартную последовательность преобразования из возвращаемого типа
F1
в тип назначения (то есть тип инициализация объекта) является лучшей последовательностью преобразования, чем стандартная последовательность преобразования из возвращаемого типаF2
в тип назначения.(Пример и остаток списка опущены)
Поскольку стандартная последовательность преобразования от int
до int
(ранг Точного соответствия) лучше стандартной последовательности преобразования от char
до int
(ранг промотирования), первый выигрывает второй, и там не будет никакой двусмысленности - для инициализации будет использоваться преобразование, определенное operator int()
, что противоречит предложению в §13.3.3.1 [over.best.ics]/p10, в котором говорится, что вызов функции будет плохо сформирован из-за двусмысленности.
Есть ли что-то неправильное в приведенном выше анализе, или это предложение является ошибкой в стандарте?