clang ++失败但g ++成功地在赋值中使用强制转换为const-unrelated-type运算符

dec*_*uto 9 g++ conversion-operator language-lawyer c++11 clang++

这是一个简短的例子,用clang重现这个"没有可行的转换"柠檬,但对编译器行为的g ++差异有效.

#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;
}
Run Code Online (Sandbox Code Playgroud)

在Godbolt的

g ++(4.9,5.2)默默地编译; 而clang ++(3.5,3.7)编译它

如果

using cast_type = A;
Run Code Online (Sandbox Code Playgroud)

要么

using cast_type = const A;
// [...] 
a = b.operator cast_type ();
Run Code Online (Sandbox Code Playgroud)

使用, 但没有默认

using cast_type = const A;
// [...] 
a = b; 
Run Code Online (Sandbox Code Playgroud)

在那种情况下,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 { 
Run Code Online (Sandbox Code Playgroud)

参考2011¹标准:clang ++是关于拒绝默认代码还是g ++接受它的权利?

Nota bene:这不是关于这个const限定符是否cast_type有意义的问题.这是关于哪个编译器符合标准,并且仅与此有关.

¹2014在这里不应该有所作为.

编辑:

请不要使用通用c ++标记重新标记它.我首先想知道哪种行为符合2011标准,并且not to break existing (< 2011) code暂时保留委员会对ansatz 的奉献精神.

Sha*_*our 9

所以看起来这个被这个clang bug报告覆盖了rvalue重载隐藏了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);
}
Run Code Online (Sandbox Code Playgroud)

失败的错误与OP的代码相同.理查德史密斯到最后说:

更新:我们选择"f(A &&)"是正确的,但我们拒绝参数的初始化是错误的.进一步减少:

  struct A {};
  struct B { operator const A(); } b;
  A &&a = b;
Run Code Online (Sandbox Code Playgroud)

这里,[dcl.init.ref] p5 bullet 2 bullet 1 bullet 2不适用,因为[over.match.ref] p1没有找到候选转换函数,因为"A"与"const A"不是引用兼容的.所以我们落入[dcl.init.ref] p5 bullet 2 bullet 2,并从'b'复制初始化A类的临时,并将引用绑定到那个.我不确定在那个过程中我们哪里出错了.

但由于缺陷报告1604,后来又发表了另一条评论:

DR1604改变了规则

 A &&a = b;
Run Code Online (Sandbox Code Playgroud)

现在形象不对.所以我们现在正确拒绝初始化.但这仍然是一个可怕的答案; 我再次向CWG提出了批评.我们应该在重载决策期间丢弃f(A &&).

所以看起来clang在技术上是基于今天的标准语言做正确的事情,但它可能会改变,因为至少来自clang团队似乎存在分歧,这是正确的结果.所以这可能会导致缺陷报告,我们将不得不等到它得到解决才能得出最终结论.

更新

看起来基于此问题提交了缺陷报告2077.