Search code examples
c++enumsimplicit-conversionoverload-resolution

Overload resolution involving old-style enums and integral types


Consider the following piece of code

#include <iostream>

using namespace std;

enum myEnum { a, b, c };

void test(myEnum e) {
  cout << "myEnum overload" << endl;
}

void test(unsigned int i) {
  cout << "unsigned int overload" << endl;
}

int main() {
  test(a);
  test(1);
  test(1u);  

  return 0;
}

(I know enum class is safer than enum for this kind of things but I am using open-source code that has old-style enums.)

When I compile this with g++ 4.4.7 and run it, I get

myEnum overload
unsigned int overload
unsigned int overload

i.e. the compiler prefers to convert int to unsigned int over converting it to myEnum. This is what I want, but I was wondering if this is always guaranteed. The standard does not specify exactly what the underlying type of myEnum should be, so I thought perhaps if it was exactly int, perhaps this would be favored over unsigned int.

But when I comment out the unsigned int overload, I get this error:

enum_overload.cpp: In function ‘int main()’:
enum_overload.cpp:17: error: invalid conversion from ‘int’ to ‘myEnum’
enum_overload.cpp:17: error:   initializing argument 1 of ‘void test(myEnum)’
enum_overload.cpp:18: error: invalid conversion from ‘unsigned int’ to ‘myEnum’
enum_overload.cpp:18: error:   initializing argument 1 of ‘void test(myEnum)’

Does that mean that old-style enums implicitly convert to their underlying types but not from those types? If that is the case, then that would answer my previous question: if integral types can't convert to myEnum, then the overload resolution is guaranteed to behave as above.


Solution

  • [conv.integral]/1:

    A prvalue of an unscoped enumeration type can be converted to a prvalue of an integer type.

    The converse is not true. There's no implicit conversion from an integer type to an unscoped enum type:

    It seems that you are confusing this with casting an integral value to an enum type: [expr.static.cast]/10

    A value of integral or enumeration type can be explicitly converted to a complete enumeration type. The value is unchanged if the original value is within the range of the enumeration values ([dcl.enum]). Otherwise, the behavior is undefined. A value of floating-point type can also be explicitly converted to an enumeration type. The resulting value is the same as converting the original value to the underlying type of the enumeration ([conv.fpint]), and subsequently to the enumeration type.

    (emphasis mine)

    But this can only be done via an explicit cast:

    E x1 = static_cast<E>(1) // yes
    E x2 = E(1);             // yes
    E x3 = 1;                // no
    E x4(1);                 // no