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

Copy initialization of class type by user defined conversion

本文关键字:类型 初始化 复制 转换 用户 定义      更新时间:2023-10-16

我有以下代码片段:

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::运算符 T() 来初始化 t,而不是报告初始化不明确的错误。在我看来,会发生以下情况(如果我错了,请纠正我):

  • t 使用类型 S 的左值进行复制初始化
  • S 不是 T,S
  • 也不是 T 的子类,所以 S 和 T 不相关
  • 由于变量 T 是复制初始化的,并且类型 S 和 T 不相关,因此编译器会尝试查找用户定义的转换序列来执行初始化。
  • 重载分辨率负责选择最佳的用户定义转换,该转换可以是 T 的转换构造函数或 S 的转换函数
  • 参数 s 中的构造函数 T::T(const S&) 的隐式转换序列是恒等转换,因为左值 s 可以直接绑定到此左值引用
  • 参数 s 中的转换函数 S::运算符 T() 的隐式转换序列也是恒等转换,因为隐式对象参数是 S&

构造函数和转换函数都返回一个类型 T 的 prvalue,可用于直接初始化变量 t。这意味着两个用户定义的转换序列的第二个标准转换序列是身份转换。

这意味着两个用户定义的转换序列同样好。还是有特殊规则更喜欢转换功能?

我正在阅读 c++11 标准中的以下规则:

表单中发生的初始化 T x = a; 在参数传递中,函数返回、抛出异常 (15.1)、处理异常 (15.3) 和聚合成员初始化 (8.5.1) 称为复制初始化。

初始值设定项的语义如下...如果目标类型是(可能是 cv 限定的)类类型:如果初始化是直接初始化,或者如果是复制初始化,其中 cv 不合格 源类型的版本与目标的类是同一类或派生类, 考虑构造函数。 否则(即,对于剩余的复制初始化情况),如 13.3.1.4 中所述枚举可以从源类型转换为目标类型或(使用转换函数时)转换为派生类的用户定义的转换序列,并通过重载分辨率 (13.3) 选择最佳转换序列

如果用户定义的转换序列

U1 包含相同的用户定义转换函数或构造函数,并且 U1 的第二个标准转换序列优于 U2 的第二个标准转换序列,则它们比另一个用户定义的转换序列 U2 是更好的转换序列

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

使用转换运算符从S转换比转换为以S const作为参数的T更好。如果您将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;
}
我想

我找到了澄清这一点的规则:

13.3.3.2: ...S1 和 S2 是引用绑定 (8.5.3),引用引用的类型是相同的类型,除了顶级 cv 限定符,并且 S2 初始化的引用引用的类型比 S1 初始化的引用引用的类型更符合 cv 资格。

在成员函数 S::operator T() 中,隐式对象参数的类型 S& 直接绑定到类型 S 的左值 s。在构造函数 T::T(const S&) 中,参数直接绑定到 S 类型的左值 s,但此引用绑定比运算符函数更符合 cv 条件,因此重载分辨率首选运算符函数。

你同意吗?