Search code examples
c++type-conversionimplicit-conversionsignedness

c++ find implicit type conversion in the expression


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?


Solution

  • 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 to long double
    • Otherwise, if either operand is double, the other operand is converted to double
    • Otherwise, if either operand is float, the other operand is converted to float

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