我有以下代码段:
struct T {
T(const T&) = default;
T(const S &);
};
struct S {
operator T();
};
int main() {
S s;
T t = s; // copy-initialization of class type
return 0;
}
我的问题是为什么编译器更喜欢S :: operator T()来初始化t而不是报告初始化是不明确的错误。在我看来,如果我错了,请纠正我:
构造函数和转换函数都返回一个类型为T的prvalue,它可用于直接初始化变量t。这意味着用户定义转换序列的第二个标准转换序列是身份转换。
这意味着用户定义的转换序列同样好。或者是否有一个特殊的规则更喜欢转换函数?
我正在阅读c ++ 11标准中的以下规则:
表单中发生的初始化 T x = a; 以及参数传递,函数返回,抛出异常(15.1),处理异常(15.3)和聚合成员初始化(8.5.1)称为复制初始化。
初始值设定项的语义如下...如果目标类型是(可能是cv限定的)类类型:如果初始化是直接初始化,或者它是复制初始化,其中cv-unqualified 源类型的版本与目标类的类相同,或者是派生类, 构造函数被认为.... 否则(即,对于剩余的复制初始化情况),可以如13.3中所述枚举可以从源类型转换为目的地类型或(当使用转换函数时)到其派生类的用户定义的转换序列。 1.4,通过重载决策(13.3)选择最好的一个
用户定义的转换序列U1是比另一个用户定义的转换序列U2更好的转换序列,如果它们包含相同的用户定义的转换函数或构造函数,并且U1的第二个标准转换序列优于第二个标准转换序列U2
也许我在做出错误的假设。我希望你能帮助我!
答案 0 :(得分:2)
使用转换运算符从S
进行的转换优于以T
作为参数转换为S const
。如果您将s
设为S const
,则首选构造函数:在一种情况下,您的身份操作确实是一种身份操作,而在另一种情况下则不是。如果您使S
const
成员的转换运算符变得模糊不清。以下是一个展示所有案例的测试程序:
struct S;
struct T {
T(const T&) = default;
T(const S &);
};
struct S {
S(); // needed to allow creation of a const object
#ifdef AMBIGUOUS
operator T() const;
#else
operator T();
#endif
};
int main() {
#ifdef CONST
S const s;
#else
S s;
#endif
T t = s; // copy-initialization of class type
return 0;
}
答案 1 :(得分:0)
我想我找到了澄清这一点的规则:
13.3.3.2:... S1和S2是引用绑定(8.5.3),引用引用的类型除了顶级cv限定符之外是相同的类型,以及引用的类型由S2引用初始化的引用比通过S1引用的引用引用的类型更符合cv。
在成员函数S :: operator T()中,隐式对象参数的类型为S&它直接绑定到S类型的左值。在构造函数T :: T(const S&)中,参数直接绑定到S类型的左值,但是这个引用绑定比运算符函数更符合cv限定,因此过载分辨率首选运算符函数。
你同意这个吗?