最新版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
orT2
is a class type andT1
is not reference-related toT2
, user-defined conversions are considered using the rules for copy-initialization of an object of type “cv1T1
” 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 toT2
:
— 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/