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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With