I am trying to understand overloading resolution in C++ through the books listed here. One such example that i wrote to clear my concepts whose output i am unable to understand is given below.
#include <iostream>
struct Name
{
operator int()
{
std::cout<<"Name's int version called"<<std::endl;
return 4;
}
operator float()
{
std::cout<<"Name's float version called"<<std::endl;
return 1.1f;
}
};
int main()
{
double a = Name(); //this works and calls Name's float version. But WHY ISN'T THIS AMBIGIOUS?
long double b = Name(); //this does not work. WHY IS THIS AMBIGIOUS?
bool c = Name(); //this does not work. WHY IS THIS AMBIGIOUS?
return 0;
}
As you can see here the program works when creating double a
. But when i try to create objects b
and c
it gives error.
My questions are:
Why don't we get the ambiguity error for object a
. That is, among the two conversion operators in class Name
, why the float
version is chosen over the int
version.
Why/how do we get the ambiguity error for object b
which is a long double
. That is just like for a
, i suspected that the float
version should have been called but instead we get the error. How is this different from the double a
case above.
Why/how do we get the ambiguity error for object c
which is bool
. In this case, I suspected that the int
version could have been chosen but instead we get an error. How is this different than the double a
version which works and uses float
version of conversion function.
I just want to understand why/how the first version works but the other two don't.
Essentially, skipping over some stuff not relevant in this case, overload resolution is done to choose the user-defined conversion function to initialize the variable and (because there are no other differences between the conversion operators) the best viable one is chosen based on the rank of the standard conversion sequence required to convert the return value of to the variable's type.
The conversion int -> double
is a floating-integral conversion, which has rank conversion.
The conversion float -> double
is a floating-point promotion, which has rank promotion.
The rank promotion is better than the rank conversion, and so overload resolution will choose operator float
as the best viable overload.
The conversion int -> long double
is also a floating-integral conversion.
The conversion float -> long double
is not a floating-point promotion (which only applies for conversion float -> double
). It is instead a floating-point conversion which has rank conversion.
Both sequences now have the same standard conversion sequence rank and also none of the tie-breakers (which I won't go through) applies, so overload resolution is ambigious.
The conversion int -> bool
is a boolean conversion which has rank conversion.
The conversion float -> bool
is also a boolean conversion.
Therefore the same situation as above arises.
See https://en.cppreference.com/w/cpp/language/overload_resolution#Ranking_of_implicit_conversion_sequences and https://en.cppreference.com/w/cpp/language/implicit_conversion for a full list of the conversion categories and ranks.
Although it might seem that a conversion between floating-point types should be considered "better" than a conversion from integral to floating-point type, this is generally not the case.