17

So, I wrote something like this

#include <iostream>
using namespace std;

void f(int32_t i)
{
    cout << "int32: " << i << endl;
}

void f(int16_t i)
{
    cout << "int16: " << i << endl;
}

void f(int8_t i)
{
    cout << "int8: " << i << endl;
}

void f(uint32_t i)
{
    cout << "uint32: " << i << endl;
}

void f(uint16_t i)
{
    cout << "uint16: " << i << endl;
}


int main() {
    uint8_t i = 0u;
    f(i);
    return 0;
}

And it printed

int32: 0

I'm a bit confused:

  • Is this well-defined behaviour, or is it implementation specific?

  • What are the rules that determine which overload is used here and to what type the variable is converted?

Eternal
  • 2,648
  • 2
  • 15
  • 21
  • 2
    It's not an answer to the question, but I think it's good to know that constrained templates can be used to disable implicit conversions. Example: https://coliru.stacked-crooked.com/a/d25bb8d892d8dc9b (or constexpr if can be used as an alternative) – titapo Mar 05 '19 at 08:14

2 Answers2

22

In comparing the conversions needed by different overloaded functions, a "promotion" is considered a better conversion sequence than a standard "conversion". Every arithmetic type can promote to at most one other type. (Promotions are also used when passing an argument to a C-style variadic function like printf. The unary + operator can be used to force a promotion of an arithmetic expression, like +n.)

For integer types which are not character types or bool, the promoted type is:

  • If int can represent all the values of the original type, then int;
  • Otherwise, if unsigned int can represent all the values of the original type, then unsigned int;
  • Otherwise, the original type itself (promotion does nothing)

In your example, when comparing the overloaded functions, an "exact match" would be best, but there is no function taking exactly int8_t (or int8_t& or const int8_t&). The promoted type of uint8_t is int, since it's required to support a range much larger than 0-255. And apparently on your system, int32_t is an alias for int, so the function void f(int32_t); requires only a promotion on the argument. The other functions are all viable, but require an integer conversion on the argument. So void f(int32_t); is considered the best overload.

So the technical answer to the question is that it is implementation specific, but only because of the relationship between int and the <cstdint> types, not because of the overload resolution rules.

aschepler
  • 70,891
  • 9
  • 107
  • 161
9

The behavior is well-defined, but implementation-specific. With a 16-bit int it would have been different.

The particular rules in the standard are:

[over.best.ics] for overload resolution. [conv.prom] for integral promotion.

Kit.
  • 2,386
  • 1
  • 12
  • 14