For the below expression:-
int main()
{
unsigned ui = 1;
float fval = 2.5;
cout << sizeof(ui) << endl << sizeof(fval) << endl;
cout << typeid(ui+fval).name();
}
we get the following output:-
4
4
f
It seems that ui+fval is a float.
However, given that both float and unsigned int are 4 bytes, and that not all of the unsigned int values can fit inside a float, shouldn't fval be converted to unsigned int?
The rules for arithmetic operators are actually slightly different than the rules for general function overload resolution.
From cppreference on arithmetic operators:
Conversions
If the operand passed to an arithmetic operator is integral or unscoped enumeration type, then before any other action (but after lvalue-to-rvalue conversion, if applicable), the operand undergoes integral promotion. If an operand has array or function type, array-to-pointer and function-to-pointer conversions are applied.
For the binary operators (except shifts), if the promoted operands have different types, additional set of implicit conversions is applied, known as usual arithmetic conversions with the goal to produce the common type (also accessible via the
std::common_type
type trait). If, prior to any integral promotion, one operand is of enumeration type and the other operand is of a floating-point type or a different enumeration type, this behavior is deprecated. (since C++20)
- If either operand has scoped enumeration type, no conversion is performed: the other operand and the return type must have the same type
- Otherwise, if either operand is
long double
, the other operand is converted tolong double
- Otherwise, if either operand is
double
, the other operand is converted todouble
- Otherwise, if either operand is
float
, the other operand is converted tofloat
[snip]
So, quite explicitly, when we're doing unsigned + float
, the unsigned
gets converted to a float
. It's the first rule that applies, so we follow it.
However, for general overload resolution, the conversion from unsigned
to float
is equivalent to the conversion from float
to unsigned
. So for instance in the following code snippet:
unsigned add(unsigned l, unsigned r) { return l + r; }
float add(float l, float r) { return l + r; }
int main()
{
unsigned ui = 1;
float fval = 2.5;
add(ui, fval);
}
It's unable to decide which version of add
to use and fails to compile.