通过用户定义的转换复制类类型的初始化

时间:2013-09-04 23:03:09

标签: c++ c++11

我有以下代码段:

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使用类型为S
  • 的左值进行复制初始化
  • S不是T而S也不是T的子类,所以S和T是不相关的
  • 由于变量t是复制初始化的事实以及类型S和T不相关的事实,编译器会尝试查找用户定义的转换序列来进行初始化。
  • 重载决策负责选择最佳用户定义转换,可以是T的转换构造函数或S的转换函数
  • 来自参数s的构造函数T :: T(const S&)的隐含转换序列是标识转换,因为左值可以直接绑定到此左值引用
  • 来自参数s的转换函数S :: operator T()的隐含转换序列也是标识转换,因为隐式对象参数是S&

构造函数和转换函数都返回一个类型为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

也许我在做出错误的假设。我希望你能帮助我!

2 个答案:

答案 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限定,因此过载分辨率首选运算符函数。

你同意这个吗?