Оба clang и gcc принимают следующий код и выбирают A::operator B*
.
struct B
{
};
struct A : B
{
operator A*();
operator B*();
};
A a;
void* x = a;
Мое чтение стандарта - особенно предложения, выделенные ниже жирным шрифтом - предполагает, что это преобразование должно быть неоднозначным.
Оба A::operator A*
и A::operator B*
являются кандидатами на разрешение перегрузки, поскольку A*
и B*
оба конвертируются в void*
через стандартное преобразование. Поскольку подразумеваемый параметр объекта A&
является единственным аргументом, рассматривается только последовательность преобразования, которая преобразуется из аргумента подразумеваемого объекта в параметр подразумеваемого объекта - тип, предоставляемый функцией преобразования, игнорируется. В обоих случаях подразумеваемый аргумент объекта представляет собой тип выражения инициализатора A
, а параметр подразумеваемого объекта - A&
. Если обе последовательности преобразования идентичны, нет возможности различать двух кандидатов.
8.5 Инициализаторы [dcl.init]
Семантика инициализаторов такова. Тип назначения - тип объекта или ссылки, являющийся инициализируется, а тип источника - тип выражения инициализатора.
- Если тип назначения является [reference/array/class...] [удаленные данные, не применимые к этому сценарию]
- В противном случае, если тип источника является (возможно, cv-qualit) типом класса, рассматриваются функции преобразования. Применяемые функции преобразования перечисляются (13.3.1.5), а лучший выбирается путем перегрузки (13.3). Выбранное пользователем преобразование вызывается для преобразования инициализатора выражение в инициализированный объект. Если преобразование невозможно или неоднозначно, инициализация плохо сформирована.
13.3.1.5 Инициализация с помощью функции преобразования [over.match.conv]
В условиях, указанных в 8.5, как часть инициализации объекта типа некласса, преобразование функция может быть вызвана для преобразования выражения инициализатора типа класса в тип объекта, являющегося инициализируется. Разрешение перегрузки используется для выбора функции преобразования, которая должна быть вызвана. Предполагая, что "cv1 T" - тип инициализируемого объекта, а "cv S" - тип выражения инициализатора, причем S a тип класса, выбранные функции выбираются следующим образом:
- Рассматриваются функции преобразования S и его базовые классы. Те неявные преобразования функции, которые не скрыты внутри S и выдают тип T или тип, который может быть преобразован в тип T через стандартную последовательность преобразования (13.3.3.1.1) являются кандидатными функциями. Для прямой инициализации эти явные функции преобразования, которые не скрыты внутри S, и дают тип T или тип, который может быть преобразованные в тип T с квалификационным преобразованием (4.4), также являются кандидатными функциями. преобразование функции, возвращающие тип с квалификацией cv, считаются выпущенными cv-неквалифицированной версией этого типа для этого процесса выбора кандидатских функций. Функции преобразования, возвращающие "ссылку на cv2 X" возвращают lvalues или xvalues, в зависимости от типа ссылки, типа "cv2 X" и поэтому считается, что для этого процесса выбора кандидатских функций X получает X.
Список аргументов имеет один аргумент, который является выражением инициализации. [Примечание. Этот аргумент будет по сравнению с неявным параметром объекта функций преобразования. -end note]
Является ли это неоднозначным в соответствии со стандартом?
РЕДАКТИРОВАТЬ: обратите внимание, что это аналогичный вопрос, но не тот, что Отличие между пользовательскими последовательностями преобразования по начальной стандартной последовательности конверсий
Отличие состоит в том, что в моем примере обе функции преобразования имеют одинаковую квалификацию.