I considered a type conversion from one type to the other, which are defined by two way, i.e., type conversion constructor and type conversion function.
struct to_type;
struct from_type{
operator to_type()const;
};
struct to_type{
to_type() = default;
to_type(const from_type&){}
};
from_type::operator to_type()const{return to_type();}
int main(){
from_type From;
to_type To;
To = From;
return 0;
}
gcc (v13.0.0, but seems to be same even in v4.9.4) don't throw any error and just call type conversion constructor in the above code.
On the other hand, clang (v16.0.0, but seems to be same even in v7.6.0) throw a "ambiguous" compile error like the following.
prog.cc:14:10: error: reference initialization of type 'to_type &&' with initializer of type 'from_type' is ambiguous
To = From;
^~~~
prog.cc:3:4: note: candidate function
operator to_type()const;
^
prog.cc:7:4: note: candidate constructor
to_type(const from_type&){}
^
prog.cc:5:8: note: passing argument to parameter here
struct to_type{
^
1 error generated.
It seems to be so curious that two major compiler show different result for this simple code. Is either compiler don't match with the standard for C ? I guess this overload resolution related to [over.ics.rank], but I could not concluded which compiler's behavior match to the standard.
Or do my source code contains undefined behavior?
CodePudding user response:
In this case clang is right, this behaviour is defined in [over.best.ics.general], and standard even mentions that such a conversion is ambiguous explicitly under the sample code to [over.best.ics.general]/10 (the scenario under the link is in fact considers another kind of ambiguity, but resolution to user-defined pair of conversion constructor and conversion operator is one of the candidates, so I removed the part of the code with another candidate):
class B;
class A { A (B&);};
class B { operator A (); };
...
void f(A) { }
...
B b;
f(b); // ... an (ambiguous) conversion b → A (via constructor or conversion function)
CodePudding user response:
Clang is wrong in rejecting the program because an overload with T&&
is better match than an overload with const int&
. Note that gcc is also wrong because it uses the copy constructor instead of operator to_type() const
. See demo. Only msvc is right and both gcc and clang are wrong. MSVC correctly uses the conversion function.
S1 S2
int int&& indistinguishable
int const int& indistinguishable
int&& const int& S1 better
Consider the contrived example:
#include <iostream>
struct to_type;
struct from_type{
operator to_type()const;
};
struct to_type{
to_type() = default;
to_type(const from_type&){std::cout <<"copy ctor";}
};
from_type::operator to_type()const{
std::cout<<"to_type operator";
return to_type();}
void f(to_type&&){}
int main(){
from_type From;
f(From); //valid and this should use from_type::operator to_type() const
}
The above program is rejected by clang(with the same error as you're getting) but accepted by gcc and msvc. Note that even though gcc accepts the above program it is still wrong because the conversion function should be used instead of the copy ctor. MSVC on the other hand correctly uses the conversion function.