strongly typed enums not allowed to be used as arguments of the same underlying type?

310 Views Asked by At

Consider the following code. I was assuming that by having the type of the strongly typed enum specified that I would be able to use it to pass to functions and templates of the same type since the specified underlying type is the same; although the compiler complains that the cast to the type is missing. In the code below removing the keyword class and the specifier on the enum of Test::TOTAL_ITEMS back to TOTAL_ITEMS will work. I understand that they don't want any type conversions for strongly typed enums but when the underlying type is the same as the type that is expected to be received by the function or template I would expect the compiler to not complain about this and not force a specific cast. Thoughts?

#include <iostream>
#include <cstdint>

template <typename T, std::size_t N = 10>
class Y {
public:
   Y() : z_() {
   }
   ~Y() = default;

private:
   T z_[N];
};

class X {
public:
   enum class Test : std::size_t {
      ITEM1 = 0,
      ITEM2,
      TOTAL_ITEMS,
   };

private:
   Y<int, Test::TOTAL_ITEMS> collection;
};

int main() {
   X xx;
}
2

There are 2 best solutions below

1
On BEST ANSWER

The whole purpose of scoped enumerations is to prevent implicit conversions to the underlying type. You'll need to cast it to get your code to compile

#include <type_traits>
Y<int, static_cast<std::underlying_type<Test>::type>(Test::TOTAL_ITEMS)> collection;

Or, if all you care about is specifying an underlying type for your enum, then drop the class keyword from its definition, and your code will compile as is.

enum Test : std::size_t {
// ...
};
0
On

The purpose of enum class is to keep its instances from implicitly casting to some other type. This is why removing class compiles: regular enum instances implicitly cast to anything their underlying type can.

When you want to convert an enum class to some other type, you must do so explicitly:

enum class Foo : int { FEE, FYE, FOE, FUM };
void f(Foo x) {};
void g(int x) {};

int main()
{
    f(Foo::FEE);                   // Okay: correct type
    //f(0);                        // Error: no implicit conversion
    f(static_cast<Foo>(0));        // Okay: explicitly converted
    //g(Foo::FYE);                 // Error: no implicit conversion
    g(1);                          // Okay: correct type
    g(static_cast<int>(Foo::FYE)); // Okay: explicitly converted
}

Live demo. See std::underlying_type for when you'd like to programatically extract a type.