The following C++ program compiles without warnings in all compilers I have tried (gcc 4.6.3, llvm 3.0, icc 13.1.1, SolarisStudio 12.1/12.3):
struct CClass
{
template<class T>
operator T() const { return 1; }
operator int() const { return 2; }
};
int main(void)
{
CClass x;
return static_cast<char>(x);
}
However, all but the SolarisStudio compilers return 2, SolarisStudio (either version) returns 1, which I would consider the most logical result.
Using return x.operator char();
results in all compilers returning 1.
Obviously, since figuring this out, I have been using the latter notation. However, I would like to know which of compilers is correct and why. (One would think that majority rules, but this still doesn't explain the why.)
This question seems to be related to the SO questions here, here, and here, but these "only" give solutions to problems, no explanations (that I was able to apply to my particular problem anyway).
Note that adding an additional overloaded casting operator, say operator float() const { return 3; }
results in all compilers except SolarisStudio complaining about ambiguity.
Cast operator: () A type cast provides a method for explicit conversion of the type of an object in a specific situation.
A Cast operator is an unary operator which forces one data type to be converted into another data type. C++ supports four types of casting: 1. Static Cast.
In order to control these types of conversions between classes, we have four specific casting operators: dynamic_cast, reinterpret_cast, static_cast and const_cast.
The first (template) overload should be picked.
Paragraph 13.3.3/1 of the C++11 Standard specifies:
[...] a viable function
F1
is defined to be a better function than another viable functionF2
if for all argumentsi
,ICSi(F1)
is not a worse conversion sequence thanICSi(F2)
, and then— for some argument
j
,ICSj(F1)
is a better conversion sequence thanICSj(F2)
, or, if not that,— the context is an initialization by user-defined conversion (see 8.5, 13.3.1.5, and 13.3.1.6) and the standard conversion sequence from the return type of
F1
to the destination type (i.e., the type of the entity being initialized) is a better conversion sequence than the standard conversion sequence from the return type ofF2
to the destination type. [ Example:struct A { A(); operator int(); operator double(); } a; int i = a; // a.operator int() followed by no conversion // is better than a.operator double() followed by // a conversion to int float x = a; // ambiguous: both possibilities require conversions, // and neither is better than the other
—end example ] or, if not that,
—
F1
is a non-template function andF2
is a function template specialization, or, if not that,[...]
As you can see the, fact that the first conversion operator is a template only becomes relevant when the standard conversion sequence from its return type (char
, in this case) to the destination type (char
, in this case) is not better than the standard conversion sequence from the return type of the non-template overload (int
, in this case) to the destination type (char
, in this case).
However, a standard conversion from char
to char
is an Exact Match, while a standard conversion from int
to char
is not. Therefore, the third item of § 13.3.3/1 does not apply, and the second item does.
This means that the first (template) overload should be picked.
The first is an exact match, the second requires a conversion. Exact matches have priority over conversions.
Those other questions you linked are mostly unrelated to yours.
Some advice: don't use template conversion operators. Name it convert_to
instead.
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