Вопрос возник, когда я изучал ответ на этот ТАК вопрос. Рассмотрим следующий код:
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-квалифицированным) типом класса, рассматриваются функции преобразования. Применимая конверсия
перечисляются функции (13.3.1.5), и выбирается лучшая
через разрешение перегрузки (13.3). Пользовательское преобразование так
выбранный вызывается для преобразования выражения инициализатора в
объект инициализируется. Если преобразование не может быть сделано или
неоднозначно, инициализация плохо сформирована.- […]
И в этом раунде разрешения перегрузки есть разделитель связей в §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, в котором говорится, что вызов функции будет неправильно сформирован из-за неоднозначности.
Что-то не так в приведенном выше анализе или это предложение является ошибкой в стандарте?
При выборе лучшего пользовательского преобразования для пользовательской последовательности преобразования мы имеем набор кандидатов на перегрузку.
§13.3.3 / p1 говорит:
Определите ICSi (F) следующим образом:
- […]
пусть ICSi (F) обозначает последовательность неявного преобразования, которая преобразует i-й аргумент в списке в
тип i-го параметра жизнеспособной функции F. 13.3.3.1 определяет последовательности неявного преобразования и
13.3.3.2 определяет, что значит для одной неявной последовательности преобразования быть лучшей последовательностью преобразования
или хуже, последовательность преобразования, чем другой.Учитывая эти определения, жизнеспособная функция F1 определяется как лучшая функция, чем другая жизнеспособная функция
F2, если для всех аргументов i ICSi (F1) не хуже последовательности преобразования, чем ICSi (F2), а затем— […]
— контекст является инициализацией путем пользовательского преобразования (см. 8.5, 13.3.1.5 и 13.3.1.6) и
стандартная последовательность преобразования из типа возврата F1 в тип назначения (то есть тип
инициализируемая сущность) является лучшей последовательностью преобразования, чем стандартная последовательность преобразования из
тип возврата F2 к типу назначения.
Это относится с
§13.3.3.1.2 / p2
2 Вторая стандартная последовательность преобразования преобразует результат пользовательского преобразования в цель
введите для последовательности. Поскольку неявная последовательность преобразования является инициализацией, специальные правила для
инициализация с помощью пользовательского преобразования применяется при выборе лучшего пользовательского преобразования для определенного пользователем
последовательность преобразования (см. 13.3.3 и 13.3.3.1).
и, следовательно, последовательность преобразования с участием operator int
выбран в качестве лучшего соответствия.
Наконец, я бы перефразировал §13.3.3.1 / p10 как
Если существует несколько различных последовательностей преобразований, каждая из которых преобразует аргумент в тип параметра и не было возможности определить лучшего кандидата,
неявная последовательность преобразования, связанная с параметром, определяется как уникальная последовательность преобразования
обозначил неоднозначную последовательность преобразования. В целях ранжирования последовательностей неявного преобразования
как описано в 13.3.3.2, неоднозначная последовательность преобразования обрабатывается как определенная пользователем последовательность, которая
неотличим от любой другой определенной пользователем последовательности преобразования134. Если функция, которая использует неоднозначные
последовательность преобразования выбрана в качестве наилучшей жизнеспособной функции, вызов будет некорректным, поскольку преобразование
один из аргументов в вызове неоднозначен.