Search code examples
c++enumsimplicit-conversioninteger-promotion

C++ primer enums and implicit conversion


Hello I have this from C++ primer 5th edition:

// unscoped enumeration; the underlying type is machine dependent

enum Tokens {INLINE = 128, VIRTUAL = 129};

void newf(unsigned char);

void newf(int);

unsigned char uc = VIRTUAL;

newf(VIRTUAL); // calls newf(int)

newf(uc); // calls newf(unsigned char)

The enum Tokens has only two enumerators, the larger of which has the value 129. That value can be represented by the type unsigned char, and many compilers will use unsigned char as the underlying type for Tokens. Regardless of its underlying type, objects and the enumerators of Tokens are promoted to int. Enumerators and values of an enum type are not promoted to unsigned char, even if the values of the enumerators would fit.

  • But If I explicitly specify the underlying type that matches exactly the integral value of the function parameter, then I think no implicit conversion will take place:

    enum days : unsigned char{sat = 1, sun, mon, tue, wed, thu, fri};
    
    void foo(unsigned char x){
        std::cout << "foo(unsigned char)\n";
    }
    
    void foo(int){
       std::cout << "foo(int)\n";
    }
    
    foo(tue); // foo(unsigend char)
    

So as you can see the enumerator value tue is not promoted to int and I get the version of foo(unsigned char) and not foo(int).

  • So does he mean if the enumeration underlying type differs from the function parameter then enumerators are not promoted to char but to int or large integral types?

  • I think in his example of enum tokens if the underlying type is unsigned char then there will be no integral promotion in the function call: newf(VIRTUAL); // calls newf(int) and the version of newf(unsigned char) is selected. What you think?


Solution

  • From cppreference's description of enums:

    Values of unscoped enumeration type are implicitly-convertible to integral types. If the underlying type is not fixed, the value is convertible to the first type from the following list able to hold their entire value range: int, unsigned int, long, unsigned long, long long, or unsigned long long, extended integer types with higher conversion rank (in rank order, signed given preference over unsigned) (since C++11). If the underlying type is fixed, the values can be converted to their underlying type (preferred in overload resolution), which can then be promoted.

    So you're right about which overload of foo will be selected, but you're wrong that there's no implicit conversion taking place. A days is still a different type from an unsigned char, but it will implicitly convert to one.

    You're also wrong that, in the event that the implementation happens to select unsigned char as the underlying type for enum Tokens, it will therefore implicitly convert to unsigned char. Implicit conversion to a narrower underlying type than int only occurs if you fix the underlying type yourself instead of letting the implementation decide.

    The text from the Primer you quoted is only describing the situation when you don't explicitly specify an underlying type.

    Quotes from the draft standard, expr.conv.prom:

    A prvalue of an unscoped enumeration type whose underlying type is not fixed can be converted to a prvalue of the first of the following types that can represent all the values of the enumeration ([dcl.enum]): int, unsigned int, long int, unsigned long int, long long int, or unsigned long long int. If none of the types in that list can represent all the values of the enumeration, a prvalue of an unscoped enumeration type can be converted to a prvalue of the extended integer type with lowest integer conversion rank ([conv.rank]) greater than the rank of long long in which all the values of the enumeration can be represented. If there are two such extended types, the signed one is chosen.

    A prvalue of an unscoped enumeration type whose underlying type is fixed ([dcl.enum]) can be converted to a prvalue of its underlying type. Moreover, if integral promotion can be applied to its underlying type, a prvalue of an unscoped enumeration type whose underlying type is fixed can also be converted to a prvalue of the promoted underlying type.

    And from over.ics.rank:

    A conversion that promotes an enumeration whose underlying type is fixed to its underlying type is better than one that promotes to the promoted underlying type, if the two are different.