clang++ 失败,但 g++ 成功地在赋值中使用转换为 const 无关类型的运算符
clang++ fails but g++ succeeds on using a cast to const-unrelated-type operator in an assignment
这是一个重现编译器行为中的 差异的简短示例。
#include <iostream>
struct A {
int i;
};
#ifndef UNSCREW_CLANG
using cast_type = const A;
#else
using cast_type = A;
#endif
struct B {
operator cast_type () const {
return A{i};
}
int i;
};
int main () {
A a{0};
B b{1};
#ifndef CLANG_WORKAROUND
a = b;
#else
a = b.operator cast_type ();
#endif
std::cout << a.i << std::endl;
return EXIT_SUCCESS;
}
live 在神马
g++ (4.9, 5.2) 静默编译;而 clang++ (3.5, 3.7) 编译它
如果
using cast_type = A;
或
using cast_type = const A;
// [...]
a = b.operator cast_type ();
被使用,
但不是默认
using cast_type = const A;
// [...]
a = b;
在那种情况下 clang++ (3.5) 归咎于 a = b
:
testling.c++:25:9: error: no viable conversion from 'B' to 'A'
a = b;
^
testling.c++:3:8: note: candidate constructor (the implicit copy constructor)
not viable:
no known conversion from 'B' to 'const A &' for 1st argument
struct A {
^
testling.c++:3:8: note: candidate constructor (the implicit move constructor)
not viable:
no known conversion from 'B' to 'A &&' for 1st argument
struct A {
^
testling.c++:14:5: note: candidate function
operator cast_type () const {
^
testling.c++:3:8: note: passing argument to parameter here
struct A {
参考 2011¹ 标准:clang++ 拒绝默认代码是正确的还是 g++ 接受默认代码是正确的?
Nota bene:这是不是关于 cast_type
上的 const
限定词是否使感觉。这是关于哪个编译器符合标准并且只关于那个。
¹ 2014 年应该不会有什么不同。
编辑:
请避免使用通用 c++ 标记重新标记它。
我首先想知道哪些行为符合 2011 年的标准,并且让委员会的奉献 not to break existing (< 2011) code
暂时远离 ansatz。
看起来这个 clang 错误报告 rvalue overload hides the const lvalue one? 涵盖了这一点,其中有以下示例:
struct A{};
struct B{operator const A()const;};
void f(A const&);
#ifdef ERR
void f(A&&);
#endif
int main(){
B a;
f(a);
}
失败并出现与 OP 代码相同的错误。理查德史密斯最后说:
Update: we're correct to choose 'f(A&&)', but we're wrong to reject
the initialization of the parameter. Further reduced:
struct A {};
struct B { operator const A(); } b;
A &&a = b;
Here, [dcl.init.ref]p5 bullet 2 bullet 1 bullet 2 does not apply,
because [over.match.ref]p1 finds no candidate conversion functions,
because "A" is not reference-compatible with "const A". So we fall
into [dcl.init.ref]p5 bullet 2 bullet 2, and copy-initialize a
temporary of type A from 'b', and bind the reference to that. I'm not
sure where in that process we go wrong.
但随后由于 defect report 1604:
而返回另一条评论
DR1604 changed the rules so that
A &&a = b;
is now ill-formed. So we're now correct to reject the initialization.
But this is still a terrible answer; I've prodded CWG again. We should
probably discard f(A&&) during overload resolution.
所以看起来 clang 在技术上基于今天的标准语言做了正确的事情,但它可能会改变,因为至少 clang 团队似乎不同意这是正确的结果。所以大概这会导致缺陷报告,我们必须等到它得到解决才能得出最终结论。
更新
看起来 defect report 2077 是基于这个问题提交的。
这是一个重现编译器行为中的
#include <iostream>
struct A {
int i;
};
#ifndef UNSCREW_CLANG
using cast_type = const A;
#else
using cast_type = A;
#endif
struct B {
operator cast_type () const {
return A{i};
}
int i;
};
int main () {
A a{0};
B b{1};
#ifndef CLANG_WORKAROUND
a = b;
#else
a = b.operator cast_type ();
#endif
std::cout << a.i << std::endl;
return EXIT_SUCCESS;
}
live 在神马
g++ (4.9, 5.2) 静默编译;而 clang++ (3.5, 3.7) 编译它
如果
using cast_type = A;
或
using cast_type = const A;
// [...]
a = b.operator cast_type ();
被使用, 但不是默认
using cast_type = const A;
// [...]
a = b;
在那种情况下 clang++ (3.5) 归咎于 a = b
:
testling.c++:25:9: error: no viable conversion from 'B' to 'A'
a = b;
^
testling.c++:3:8: note: candidate constructor (the implicit copy constructor)
not viable:
no known conversion from 'B' to 'const A &' for 1st argument
struct A {
^
testling.c++:3:8: note: candidate constructor (the implicit move constructor)
not viable:
no known conversion from 'B' to 'A &&' for 1st argument
struct A {
^
testling.c++:14:5: note: candidate function
operator cast_type () const {
^
testling.c++:3:8: note: passing argument to parameter here
struct A {
参考 2011¹ 标准:clang++ 拒绝默认代码是正确的还是 g++ 接受默认代码是正确的?
Nota bene:这是不是关于 cast_type
上的 const
限定词是否使感觉。这是关于哪个编译器符合标准并且只关于那个。
¹ 2014 年应该不会有什么不同。
编辑:
请避免使用通用 c++ 标记重新标记它。
我首先想知道哪些行为符合 2011 年的标准,并且让委员会的奉献 not to break existing (< 2011) code
暂时远离 ansatz。
看起来这个 clang 错误报告 rvalue overload hides the const lvalue one? 涵盖了这一点,其中有以下示例:
struct A{};
struct B{operator const A()const;};
void f(A const&);
#ifdef ERR
void f(A&&);
#endif
int main(){
B a;
f(a);
}
失败并出现与 OP 代码相同的错误。理查德史密斯最后说:
Update: we're correct to choose 'f(A&&)', but we're wrong to reject the initialization of the parameter. Further reduced:
struct A {}; struct B { operator const A(); } b; A &&a = b;
Here, [dcl.init.ref]p5 bullet 2 bullet 1 bullet 2 does not apply, because [over.match.ref]p1 finds no candidate conversion functions, because "A" is not reference-compatible with "const A". So we fall into [dcl.init.ref]p5 bullet 2 bullet 2, and copy-initialize a temporary of type A from 'b', and bind the reference to that. I'm not sure where in that process we go wrong.
但随后由于 defect report 1604:
而返回另一条评论DR1604 changed the rules so that
A &&a = b;
is now ill-formed. So we're now correct to reject the initialization. But this is still a terrible answer; I've prodded CWG again. We should probably discard f(A&&) during overload resolution.
所以看起来 clang 在技术上基于今天的标准语言做了正确的事情,但它可能会改变,因为至少 clang 团队似乎不同意这是正确的结果。所以大概这会导致缺陷报告,我们必须等到它得到解决才能得出最终结论。
更新
看起来 defect report 2077 是基于这个问题提交的。