4

When compiling the following code:

#include <iostream>
using namespace std;

void print(int i){
    cout << i << endl;
}

void print(float i){
    cout << i << endl;
}

int main(){
    print(5);
    print(5.5)
    return 0;
}

I get the error:

call of overloaded 'print(double)' is ambiguous.

However, if I change

void print(float i){

to

void print(double i){

the code compiles. Why is that?

Baum mit Augen
  • 49,044
  • 25
  • 144
  • 182
OpenGLmaster1992
  • 281
  • 2
  • 5
  • 13

3 Answers3

5

Try a different exercise to understand this. Removing either of the two overloads will make the program compile, although there's no identity match for the literal 5.5, a double value, it can be implicitly converted to int or float.

When both overloads are present, since 5.5 can be implicitly converted to either a int or float, both are viable. The compiler is unable to decide between the two, hence the error.

Upon making the literal a float, 5.5f, we've an identity match, the float overload and no ambiguity in decision for the compiler. Conversely, keeping the literal double, 5.5, changing the function prototype from float to double also works since this is an identity match as well.

legends2k
  • 31,634
  • 25
  • 118
  • 222
4

The problem is that 5.5 is of type double. Since print is overloaded, the compiler will look for the best match to find out which overload to call, a process called overload resolution. This is a quite complex set of rules, but here is what happens in your simple case:

First the compiler will check for an exact match, i.e. some void print(double); or the like.

Since this does not exist, it considers conversions. A double can be implicitly converted to both int and float, and those conversions are deemed equally good.

Thus, the compiler cannot decide which function overload it should call and complains that the call is ambiguous.

As already mentioned by others, you can fix this by either getting the input type exactly right: print(5.5f); or adding an overload that is an unambiguously better match for a double argument, like e.g.

void print (double);
void print (const double&);
template <class T>
void print (T);
Baum mit Augen
  • 49,044
  • 25
  • 144
  • 182
3

Integer version:

void print(int i)
           ^^^ // This declaration wants an `int` type

Float version:

void print(float i)
           ^^^^^ // This declaration wants a `float` type

Usage:

print(5); // Calling print with `int`, finds the "Integer version"
print(5.5); // '5.5' is a `double` type, which is neither an `int` type nor a `float` type

Since a double type is specified and there is not a double overload a conversion to either int or float is required to use the existing overloads.

Using the existing overloads:

 print(5.5f); // Use a `float` literal (i.e., this is a `float` type)
 print(static_cast<float>(5.5)); // I'd recommend the former but this works
 print(static_cast<int>(5.5)); // Works but obviously truncates the value

Add a new overload (as opposed to updating the existing float overload):

void print(double i); // A new overload declaration
James Adkison
  • 9,412
  • 2
  • 29
  • 43
  • 1
    *"'5.5' is a `double` type which matches neither the "Integer version" nor the "Float version""*. Actually it matches both, but both are given a conversion rank (equally good from an overload resolution point of view) – Piotr Skotnicki Sep 22 '15 at 13:54
  • @PiotrSkotnicki Yes, you're correct, poor wording on my part. I intended to convey that `double` is not a `float` or an `int` and each requires a single conversion. I'll try to make it more clear. – James Adkison Sep 22 '15 at 14:14