Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I specialize a class for enums of underlying type int?

Tags:

c++

c++11

#include <type_traits>

enum class MyEnum
{
    Hello
};

template <typename T, typename Enable = void>
class MyClass
{
public:
    MyClass(T obj) : e(obj)
    {}

private:
    T e;
};

template <typename T>
class MyClass <T, 
    typename std::enable_if< std::is_enum<T>::value 
                             && std::is_same<typename std::underlying_type<T>::type, 
                                             int>::value>::type > 
{
public:
    MyClass(T obj) : e(obj)
    {}

private:
    T e;
};

int main()
{
    MyClass<MyEnum> c(MyEnum::Hello);
    MyClass<int> c1(1); //does not compile due to std::underlying_type
    return 0;
}

I would like to be able to specialize MyClass for enums of underlying type 'int'. I can't do that using std::underlying_type because it is only specific to enums. Any ideas on how I can proceed?

like image 316
RaB Avatar asked Apr 11 '18 11:04

RaB


People also ask

What is the underlying type of an enum?

Each enum type has a corresponding integral type called the underlying type of the enum type. This underlying type shall be able to represent all the enumerator values defined in the enumeration. If the enum_base is present, it explicitly declares the underlying type.

What is the default underlying type of each element in an enum?

The default underlying type of the enumeration elements is int. By default, the first enumerator has the value 0, and the value of each successive enumerator is increased by 1. Enums are enumerated data type in C#.

What is enum underlying type in C++?

The type of a C++ enum is the enum itself. Its range is rather arbitrary, but in practical terms, its underlying type is an int . It is implicitly cast to int wherever it's used, though.

Can enum class have method?

The enum class body can include methods and other fields. The compiler automatically adds some special methods when it creates an enum. For example, they have a static values method that returns an array containing all of the values of the enum in the order they are declared.


2 Answers

A simple enough solution to this quirk is to insulate std::underlying_type behind your own SFINAE-friendly trait:

template <class T, class = void>
struct underlying_type {};

template <class T>
struct underlying_type<
    T,
    typename std::enable_if<std::is_enum<T>::value>::type
> {
    using type = typename std::underlying_type<T>::type;
};

Then your specialization can be written as:

template <typename T>
class MyClass<
    T, 
    typename std::enable_if<std::is_same<
        typename underlying_type<T>::type,
        int
    >::value>::type
> {
    // ...
};
like image 168
Quentin Avatar answered Nov 08 '22 10:11

Quentin


With one extra (delayed) indirection with conditional<..>::type::type:

template <typename T> struct Identity { using type = T; };

template <typename E>
class MyClass <E, 
    typename std::enable_if<
        std::is_enum<E>::value 
        && std::is_same<
            int,
            typename std::conditional<
                std::is_enum<E>::value,
                std::underlying_type<E>,
                Identity<E>
            >::type::type
        >::value
    >::type
>
{
    // ...
};

We really does std::underlying_type<E>::type only for enum.

Demo

like image 30
Jarod42 Avatar answered Nov 08 '22 10:11

Jarod42