0x5*_*9df 12 c++ initialization type-conversion conversion-operator language-lawyer
最新版本的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);
}
Run Code Online (Sandbox Code Playgroud)
如果进行了任何这些更改,clang将接受以下代码:
Y移动构造函数const从转换运算符中删除Y y(x)为Y y = x原始示例合法吗?哪个编译器错了?在检查标准中有关转换函数和重载分辨率的部分之后,我还未能找到明确的答案.
当我们枚举构造函数并检查它们的可行性(即是否存在隐式转换序列)时,对于移动构造函数,[dcl.init.ref]/5会落入最后一个要点 (5.2.2),其中已被核心问题1604和1571(按顺序)修改。
\n\n这些决议的底线是
\n\n\n\n\n如果
\nT1或T2是类类型并且与T1不引用相关T2,则\n 考虑使用用户定义的\n 类型对象的复制初始化规则 \xe2\x80\x9c cv1T1\xe2\x80\x9d定义的转换(8.6、13.3.1.4、13.3.1.5);如果相应的非引用副本初始化格式不正确,则该程序格式不正确。然后,如非引用复制初始化所述,对转换函数的调用结果用于直接初始化引用。
第一部分仅导致选择转换运算符。因此,根据粗体部分,我们使用const Y直接初始化Y&&。我们再次失败,直到最后一个要点,由于(5.2.2.3)而失败:
\n\n\n如果
\nT1与引用相关T2:
\xe2\x80\x94 cv1应与cv2相同\n cv 限定,或大于 cv 限定;和
然而,这不再属于我们原来的重载决议,它只看到转换运算符应用于直接初始化引用。在您的示例中,重载解析选择移动构造函数,因为[over.ics.rank]/(3.2.5),然后上面的段落使程序格式错误。这是一个缺陷,已作为核心问题 2077提交。明智的解决方案是在重载解析期间丢弃移动构造函数。
\n\n所有这些对于您的修复都是有意义的:删除const将防止失败,因为类型现在是引用兼容的,并且删除移动构造函数会留下复制构造函数,它具有 const 引用(即也可以工作)。最后,当我们写 时Y y = x;,应用 (17.6.3),而不是 [dcl.init]/(17.6.2);
\n\n\n否则(即,对于其余的复制初始化情况),可以如 13.3 中所述枚举可以从源类型转换为目标类型或(当使用转换函数时)转换为其派生类的用户定义的转换序列。 1.4,并通过重载决议(13.3)选择最好的一个。[...]。该调用用于根据上述规则直接初始化作为复制初始化目标的对象。
\n
即,初始化实际上与成功相同Y y(x.operator const Y());,因为移动构造函数不可行(Y&& y = const Y失败得足够浅)并且选择了复制构造函数。
| 归档时间: |
|
| 查看次数: |
199 次 |
| 最近记录: |