Это настоящая поездка по стандарту.
Для оператора ,
, унарного оператора &
или оператора ->
набор встроенных кандидатов пуст.Для всех других операторов встроенные кандидаты включают в себя все операторные функции-кандидаты, определенные в 16.6, которые по сравнению с данным оператором * имеют одинаковое имя оператора и * принимают одинаковое количество операндов и * принимают типы операндов длякоторый данный операнд или операнды могут быть преобразованы в соответствии с 16.3.3.1, и * не имеют того же списка параметров типа, что и любой другой кандидат, не являющийся кандидатом, который не является специализацией шаблона функции.Встроенные кандидаты, определенные в 16.6 / 13 для operator*
:
Для каждой пары выдвинутых арифметических типов L
и R
существуют операторные функции-кандидаты вида
LR operator*(L, R);
// ...
Clang распечатывает полный список таких встроенных кандидатов.Предположительно GCC согласен с этим списком.Теперь необходимо применить разрешение перегрузки, чтобы выбрать «вызов».(Конечно, встроенная operator*
не является реальной функцией, поэтому «вызов» это просто означает преобразование аргументов в типы «параметров», а затем выполнение встроенного оператора умножения.) Очевидно, лучший жизнеспособный кандидат R будет unsigned int
, так что мы получим точное совпадение для второго аргумента, но как насчет первого аргумента?
Для данного L ,компилятор должен рекурсивно применить разрешение перегрузки с кандидатами, описанными в [over.match.conv], чтобы определить, как преобразовать foo<uint8_t>
в L :
В условиях, указанных в 11.6как часть инициализации объекта неклассового типа может быть вызвана функция преобразования для преобразования выражения инициализатора типа класса в тип инициализируемого объекта.Разрешение перегрузки используется для выбора функции преобразования, которая будет вызвана.Предполагая, что « cv1 T
» является типом инициализируемого объекта, а « cv S
» является типом выражения инициализатора с классом S
Тип, функции-кандидаты выбираются следующим образом:
- Рассматриваются функции преобразования
S
и его базовые классы.Те неявные функции преобразования, которые не скрыты в S
и дают тип T
или тип, который можно преобразовать в тип T
через стандартную последовательность преобразования (16.3.3.1.1), являются функциями-кандидатами.Для прямой инициализации те функции явного преобразования, которые не скрыты в S
и дают тип T
или тип, который можно преобразовать в тип T
с помощью преобразования квалификации (7.5), также являются функциями-кандидатами.Считается, что функции преобразования, которые возвращают cv-квалифицированный тип, дают cv-неквалифицированную версию этого типа для этого процесса выбора функций-кандидатов.Функции преобразования, которые возвращают «ссылку на cv2 X
», возвращают lvalue или xvalues, в зависимости от типа ссылки, типа « cv2 X
» и, следовательно, считаются получающимиX
для этого процесса выбора функций-кандидатов.
Список аргументов имеет один аргумент, который является выражением инициализатора.[ Примечание: Этот аргумент будет сравниваться с неявным параметром объекта функций преобразования. - Конечная заметка ]
Итак, одна конфетадата для преобразования foo<uint8_t>
в L заключается в том, чтобы позвонить operator uint8_t
, а затем выполнить любое стандартное преобразование, необходимое для преобразования uint8_t
в L .Другой кандидат должен вызвать operator S
, но S
должно быть выведено, как указано в [temp.deduct.conv]:
Вывод аргумента шаблона выполняется путем сравнения возвращаемого типа преобразованияшаблон функции (назовите его P
) с типом, который требуется в результате преобразования (назовите его A
; для определения этого типа см. 11.6, 16.3.1.5 и 16.3.1.6), как описано в 17.8..2.5....
Таким образом, компилятор выведет S
= L .
Чтобы выбрать, вызывать ли operator uint8_t
или operator
L , процесс разрешения перегрузки используется с объектом foo<uint8_t>
в качестве подразумеваемого аргумента объекта.Поскольку преобразование из foo<uint8_t>
в неявный тип аргумента объекта является просто преобразованием идентичности в обоих случаях (так как оба оператора являются прямыми членами без квалификации cv), правило разрыва связей [over.match.best] / (1.4) должны использоваться:
контекст является инициализацией путем пользовательского преобразования (см. 11.6, 16.3.1.5 и 16.3.1.6) и стандартной последовательности преобразования из возвращаемого типа F1
для типа назначения (т. е. типа инициализируемого объекта) является лучшей последовательностью преобразования, чем стандартная последовательность преобразования из типа возврата F2
в тип назначения ...
Таким образомкомпилятор всегда будет выбирать operator
L более operator uint8_t
, чтобы получить преобразование идентификатора из результата оператора преобразования в L (если только L само по себе uint8_t
, но этого не может быть, потому что L должен быть повышенным типом).
Таким образом, для каждого возможного L , чтобы "вызвать"«operator* LR(L, R)
, импликацияЭто последовательность преобразования, требуемая для первого аргумента, является определяемым пользователем преобразованием вызова operator
L .При сравнении operator*
с различными L , компилятор не может решить, какой из них лучше: другими словами, должен ли он вызывать operator int
для вызова operator*(int, unsigned int)
, или долженон звонит operator unsigned int
, чтобы позвонить operator*(unsigned int, unsigned int)
, или он должен звонить operator double
, чтобы звонить operator*(double, unsigned int)
, и так далее?Все одинаково хорошие варианты, и перегрузка неоднозначна.Таким образом, Clang прав, а GCC содержит ошибку.