Ambiguous overload error when using conversion function
Asked Answered
M

2

12

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:

  1. 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.

  2. 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.

  3. 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.

Macedonia answered 25/1, 2022 at 16:21 Comment(4)
I can see why the c case does not work, as it is narrowing, but I'm not sure why a is allowed and b is not.Illyes
You may want the language-lawyer tag on this question, if you want the technical answer from the standard citing chapter & verse.Lidstone
IMO Implicit promotion float to double is a reason, operator float() returns double.Dottiedottle
The similar behavior with operator char() instead of operator int(): int b = Name(); - ok, long c = Name(); - ambiguous. char is promoted to int and operator char() returns int.Dottiedottle
S
11

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.

Sweetscented answered 25/1, 2022 at 16:59 Comment(3)
See #7.7 and #7.9 in this Draft C++17 Standard for the official promotion stuff. Though why float -> long double is excluded, I'm not sure. (Or even double -> long double.) Or here and here for the latest online Standard.Almondeyed
@AdrianMole You could ask the same for integral promotions, which also (mostly) only applies for conversion from smaller types to int. It just gives preference to the default promotions that would be done in a C function with unknown parameter type.Sweetscented
So... you basically need a PhD to know simple conversions, promotions and overload resolutions in C++Statesmanship
M
1

In this initialization

double a = Name();

there is used the floating point promotion.

The C++ 17 Standard (7.7 Floating-point promotion)

1 A prvalue of type float can be converted to a prvalue of type double. The value is unchanged. 2 This conversion is called floating-point promotion

For the conversion function that returns an object of the type int there is used a conversion that has a less rank than the floating-point promotion.

That is a conversion function with the promotion rank is more viable than a function that has the conversion rank.

In these declarations

long double b = Name();
bool c = Name();

there are used conversions from the type int and float correspondingly to the type long double and bool. So neither conversion is better. The both functions have the conversion rank.

Misdemeanor answered 25/1, 2022 at 17:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.