Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

clang++ fails but g++ succeeds on using a cast to const-unrelated-type operator in an assignment

Here's a short example that reproduces this “no viable conversion” with lemon for clang but valid for g++ difference in compiler behavior.

#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 at godbolt's

g++ (4.9, 5.2) compiles that silently; whereas clang++ (3.5, 3.7) compiles it

if

using cast_type = A;

or

using cast_type = const A;
// [...] 
a = b.operator cast_type ();

are used, but not with the defaulted

using cast_type = const A;
// [...] 
a = b; 

In that case clang++ (3.5) blames 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 { 

With reference to the 2011¹ standard: Is clang++ right about rejecting the defaulted code or is g++ right about accepting it?

Nota bene: This is not a question about whether that const qualifier on the cast_type makes sense. This is about which compiler works standard-compliant and only about that.

¹ 2014 should not make a difference here.

EDIT:

Please refrain from re-tagging this with the generic c++ tag. I'd first like to know which behavior complies to the 2011 standard, and keep the committees' dedication not to break existing (< 2011) code out of ansatz for now.

like image 329
decltype_auto Avatar asked Nov 12 '15 13:11

decltype_auto


1 Answers

So it looks like this is covered by this clang bug report rvalue overload hides the const lvalue one? which has the following example:

struct A{};
struct B{operator const A()const;};
void f(A const&);
#ifdef ERR
void f(A&&);
#endif
int main(){
  B a;
  f(a);
}

which fails with the same error as the OP's code. Richard Smith toward the end says:

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.

but then comes back with another comment due to a 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.

So it seems like clang is technically doing the right thing based on the standard language today but it may change since there seems to be disagreement at least from the clang team that this is the correct outcome. So presumably this will result in a defect report and we will have to wait till it is resolved before we can come to a final conclusion.

Update

Looks like defect report 2077 was filed based on this issue.

like image 172
Shafik Yaghmour Avatar answered Oct 20 '22 00:10

Shafik Yaghmour