Выбранная перегрузка из набора функций преобразования несовместима между GCC и Clang.
Рассмотрим этот пример
#include <iostream>
struct T{
T() = default;
T(T const&) =default;
};
T global;
struct S{
operator T(){
std::cout<<"#1\n";
return T{};
}
operator T&(){
std::cout<<"#2\n";
return global;
}
};
int main(){
S s;
T obj(s);
}
GCC выбирает, а Clang выбирает вместо этого, я считаю их неоднозначными. Согласно dcl.init#17.6.3
В противном случае (т. е. для остальных случаев инициализации копированием) определяемые пользователем преобразования, которые могут преобразовывать исходный тип в целевой тип или (при использовании функции преобразования) в его производный класс, перечисляются, как описано в [over. match.copy], а лучший выбирается через разрешение перегрузки ([over.match]) . Если преобразование не может быть выполнено или неоднозначно, инициализация имеет неправильный формат. Выбранная функция вызывается с выражением инициализатора в качестве аргумента; если функция является конструктором, вызов является значением prvalue неквалифицированной cv версии целевого типа, объект результата которого инициализируется конструктором. Вызов используется для прямой инициализации в соответствии с приведенными выше правилами объекта, который является местом назначения инициализации копирования.
Согласно over.match.copy#1.2
Когда типом выражения инициализатора является тип класса «cv S», рассматриваются неявные функции преобразования S и его базовых классов. При инициализации временного объекта ([class.mem]) для привязки к первому параметру конструктора, где параметр имеет тип «ссылка на cv2 T», а конструктор вызывается с одним аргументом в контексте прямой инициализации объекта типа «cv3 T» также рассматриваются явные функции преобразования. Те, которые не скрыты внутри S и дают тип, чья версия cv-unqualified является тем же типом, что и T, или является его производным классом, являются функциями-кандидатами. Вызов функции преобразования, возвращающий «ссылку на X», является glvalue типа X, и поэтому считается, что такая функция преобразования дает X для этого процесса выбора функций-кандидатов.
Следовательно, независимо от
для некоторого аргумента j ICSj(F1) является лучшей последовательностью преобразования, чем ICSj(F2), или, если это не так,
Единственная надежда, которая может определить, какая перегрузка является лучшей, падает на [over.match.best#2.2]
контекст представляет собой инициализацию путем определяемого пользователем преобразования (см. [dcl.init], [over.match.conv] и [over.match.ref]) и стандартной последовательности преобразования из возвращаемого типа F1 в целевой тип. (т. е. тип инициализируемого объекта) является лучшей последовательностью преобразования, чем стандартная последовательность преобразования из возвращаемого типа F2 в целевой тип.
Обе их стандартные последовательности преобразования являются преобразованиями идентичности. Следовательно, они должны быть неоднозначными. Однако GCC и Clang имеют свои интерпретации и несовместимы. Интересно, какая интерпретация правильная?