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 是基于这个问题提交的。