5

I am not able to explain why the second call (B) doesn't give any errors as there are two char elements, and there is no certain match for this call.

Why it is called the second one (2.), but not the first (1.) version?

I've noticed that there are some automatic conversions. The thing that i don't get is why 'a' is promoted to int and 'c' isn't.

// 1.
int fun(int a, int b)
{
    return a + b;
}

// 2.
int fun(int a, char b)
{
    return b - a;
}

// 3 
int fun(float a, float b)
{
    return a * b;
}

int main() {

    //      A.          B.              C.
    cout << fun(1,0) << fun('a','c') << fun(2.f,2.f);

    return 0;
}
Cătălina Sîrbu
  • 1,253
  • 9
  • 30

4 Answers4

6

The rules for overload resolution are complicated. In this case, the reason func('a','c') prefers int fun(int a, char b) is because it implies the fewest implicit conversion sequences. Looking at each case :

int fun(int a, int b) has two arguments that are not perfect matches. It requires two promotions from char to int.

int fun(int a, char b) has one exact match and one promotion from char to int.

int fun(float a, float b) has two arguments that are not perfect matches that require conversions (worse than promotion) from char to float.

François Andrieux
  • 28,148
  • 6
  • 56
  • 87
  • 1
    Thank you. I will take your answer as it was the first one in the comments! – Cătălina Sîrbu Dec 30 '19 at 15:12
  • I also have something related to this: `int fun(int a, char b='z')`,`int fun(float a, float b=0)` and `int fun(long a, long b = 1)`. Why at the call of `fun(2e0)` i get an ambiguous error? The scientific notation is of type Double. Why can't it just be converted automatically to float? – Cătălina Sîrbu Dec 30 '19 at 15:30
  • 1
    Going from `float` to `double` is a promotion but going from `double` to `float` is a conversion (it potentially has to perform rounding). So all 3 overloads need 1 conversion. It's unrelated to the default arguments. If you had a `int fun(double);` and called `fun(2.f);` then it would not complain. – François Andrieux Dec 30 '19 at 15:36
  • When you go wider is a promotion and when you get narrower is a conversion? – Cătălina Sîrbu Dec 30 '19 at 15:41
  • @CătălinaSîrbu It's not just about width because of `signed` vs `unsigned` integer types. For example, `unsigned int` and `signed int` have the same size but going from one to the other is a conversion. [These](https://en.cppreference.com/w/cpp/language/implicit_conversion#Integral_promotion) are all the integer promotions possible. [Floating-point promotion](https://en.cppreference.com/w/cpp/language/implicit_conversion#Floating-point_promotion) is going from `float` to `double`. It's not clear to me how going to `long double` is treated, but it looks like it's always a conversion. – François Andrieux Dec 30 '19 at 16:03
  • @CătălinaSîrbu I messed up the links in my previous comment. I'll fix it in a minute. Edit : Fixed. – François Andrieux Dec 30 '19 at 16:06
3

You've already got the answer, an implicit conversion is happening. chars are special small numbers; you can verify this by coutint an int8_t from <cstdint>. As to why 2 was selected and not 1, it has to do with what matches more. Overload 2 exactly matches one of the parameters and none of the others do. And because the first parameter can be implicitly converted, this is the closest match and therefore the overload that the compiler chooses.

sweenish
  • 4,793
  • 3
  • 12
  • 23
  • i've followed the tutorial from the www.cplusplus.com. There is quite a little amount of information about why certain selections are done. To be sincere there is a singe example. – Cătălina Sîrbu Dec 30 '19 at 15:08
2

'c' is a char. Calling function 1 will require promoting it to an int. On the other hand, for function 2 it doesn't have to undergo a conversion (it's the identity conversion). When ranking viable functions to call in an overload set, the standard says as follow

[over.match.best]

1 Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then

  • for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2), or,

Which is our case here. The implicit conversion sequence for the second argument is better in one overload compared to the other. So it gets chosen.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
1

If you remove fun(int a, char b) the fun(int a, int b) function will be called. I only say this as an example of what the others are saying. fun(int a, char b) is chosen because it matches better than fun(int a, int b). The char is implicitly cast to its ASCII integer value in order to match the function parameters.

Grant Singleton
  • 1,611
  • 6
  • 17