c++ - Clang 和 GCC 不同意使用转换运算符直接初始化的合法性

标签 c++ initialization type-conversion language-lawyer conversion-operator

最新版clang(3.9)在f第二行拒绝此代码;最新版本的 gcc (6.2) 接受它:

struct Y {
    Y();
    Y(const Y&);
    Y(Y&&);
};

struct X {
    operator const Y();
};

void f() {
    X x;
    Y y(x);
}

如果进行了这些更改,clang 将接受代码:

  • 移除Y的移动构造函数
  • 从转换运算符中移除 const
  • Y y(x) 替换为 Y y = x

原始示例合法吗?哪个编译器错了?在检查了标准中关于转换函数和重载解析的部分后,我还没有找到明确的答案。

最佳答案

当我们枚举构造函数并检查它们的可行性时 - 即是否存在隐式转换序列 - 对于移动构造函数,[dcl.init.ref]/5一直到最后一个要点 (5.2.2),它已被核心问题 1604 修改和 1571 (按此顺序)。

这些决议的底线是

If T1 or T2 is a class type and T1 is not reference-related to T2, user-defined conversions are considered using the rules for copy-initialization of an object of type “cv1 T1” by user-defined conversion (8.6, 13.3.1.4, 13.3.1.5); the program is ill-formed if the corresponding non-reference copy-initialization would be ill-formed. The result of the call to the conversion function, as described for the non-reference copy-initialization, is then used to direct-initialize the reference.

第一部分只是导致选择转换运算符。所以,根据粗体部分,我们使用const Y来直接初始化Y&&。同样,我们一直失败,直到最后一个要点,由于(5.2.2.3)而失败:

If T1 is reference-related to T2:
cv1 shall be the same cv-qualification as, or greater cv-qualification than, cv2 ; and

但是,这不再属于我们最初的重载决议,它只看到转换运算符将用于直接初始化引用。在您的示例中,重载解析选择移动构造函数,因为 [over.ics.rank]/(3.2.5) ,然后上面的段落使程序格式错误。这是一个缺陷,已归档为 core issue 2077 .一个明智的解决方案是在重载决议期间丢弃移动构造函数。

所有这些都对您的修复有意义:删除 const 将防止失败,因为类型现在是引用兼容的,并且删除移动构造函数会留下具有 const 的复制构造函数引用(即也可以)。最后,当我们写Y y = x;,而不是[dcl.init]/(17.6.2),应用(17.6.3);

Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated as described in 13.3.1.4, and the best one is chosen through overload resolution (13.3). [...]. The call is used to direct-initialize, according to the rules above, the object that is the destination of the copy-initialization.

即初始化实际上与 Y y(x.operator const Y()); 相同,它成功了,因为移动构造函数不可行(Y&& y = const Y失败的足够浅)并选择了复制构造函数。

关于c++ - Clang 和 GCC 不同意使用转换运算符直接初始化的合法性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40006925/

相关文章:

c++ - 当我每秒调用它 40 次时,QListWidget::addItem() 会闪烁

c++ - Linux中加载时链接与运行时链接期间的符号地址

c++ - 焊接体的 Box2d 中心质量?

c++ - 如何为类的内置类型成员变量获取/实现 "Uninitialized uses warning"消息?

java - 如何从十六进制转换为十进制以及从十进制转换为二进制

interface - 我可以键入断言一部分接口(interface)值吗?

c++ - QNX 错误 - 静态成员变量的 "Undefined reference to"

java - 如何声明数组类型类

c++ - 为什么静态数据成员可能没有被初始化?

c++ - 确定字节输入和显示值之间的未知关系