考虑一下这个example https://godbolt.org/z/WMncscaaq
#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);
}
海湾合作委员会选择#1
当 Clang 选择时#2
相反,我认为它们含糊不清。根据dcl.init#17.6.3 https://timsong-cpp.github.io/cppwp/n4861/dcl.init#17.6.3
否则(即,对于剩余的复制初始化情况),可以按照[over.匹配.复制],和通过重载决策选择最好的一个([over.match])。如果转换无法完成或不明确,则初始化格式错误。使用初始值设定项表达式作为其参数来调用所选函数;如果函数是构造函数,则调用是目标类型的 cv 无限定版本的纯右值,其结果对象由构造函数初始化。该调用用于根据上述规则直接初始化作为复制初始化目标的对象。
根据结束.match.copy#1.2 https://timsong-cpp.github.io/cppwp/n4861/over.match.copy#1.2
当初始化表达式的类型是类类型“cv S”时,考虑S及其基类的非显式转换函数。当初始化临时对象([class.mem])以绑定到构造函数的第一个参数时,其中参数的类型为“对 cv2 T 的引用”,并且在直接初始化的上下文中使用单个参数调用构造函数对于“cv3 T”类型的对象,还考虑显式转换函数。那些未隐藏在 S 中并产生 cv 未限定版本与 T 类型相同或其派生类的类型的函数是候选函数。对返回“对 X 的引用”的转换函数的调用是 X 类型的泛左值,因此这样的转换函数被认为在选择候选函数的过程中产生 X。
因此,无论#1
or #2
,它们都被认为是yield类型T
。它们都有隐式参数对象T&
,因此 [over.match.best#2.1] 无法确定哪个是最好的
对于某些参数 j,ICSj(F1) 是比 ICSj(F2) 更好的转换序列,或者,如果不是这样,
能够确定哪种过载最好的唯一希望落在 [over.match.best#2.2] 上
上下文是通过用户定义的转换(请参阅 [dcl.init]、[over.match.conv] 和 [over.match.ref])以及从 F1 的返回类型到目标类型的标准转换序列进行的初始化(即正在初始化的实体的类型)是比从 F2 的返回类型到目标类型的标准转换序列更好的转换序列
它们的标准转换序列都是身份转换。因此,它们应该是含糊的。但GCC和Clang有各自的解释,并不一致。我想知道哪种解释是正确的?