34

I have a variable declared as:

enum class FooEnum: uint64_t {}

and I would like to cast to its base-type, but I don't want to hardcode the base-type. For instance, something like this:

FooEnum myEnum;
uint64_t * intPointer = (underlying_typeof(myEnum))&myEnum;

Is this possible?

R. Martinho Fernandes
  • 209,766
  • 68
  • 412
  • 492
Kyle
  • 415
  • 1
  • 4
  • 7
  • http://stackoverflow.com/questions/28002/regular-cast-vs-static-cast-vs-dynamic-cast casting, specifically dynamic – L7ColWinters Feb 18 '12 at 17:26
  • 5
    @L7ColWinters `dynamic_cast` can't help. Don't let the `class` keyword and the `: uint64_t` mislead you into thinking that scoped enumerations and underlying types of enumerations are similar to class inheritance. – Luc Danton Feb 18 '12 at 21:39

4 Answers4

40

You can use this:

The doc says,

Defines a member typedef type of type that is the underlying type for the enumeration T.

So you should be able to do this:

#include <type_traits> //include this

FooEnum myEnum;
auto pointer = static_cast<std::underlying_type<FooEnum>::type*>(&myEnum);
Nawaz
  • 327,095
  • 105
  • 629
  • 812
  • Would a `static_cast` work? I was under the impression that no sane casts would work on a `enum class`. – deft_code Feb 21 '12 at 18:50
19

Your guessed syntax is amazingly close. You're looking for std::underlying_type in <type_traits>:

#include <type_traits>
#include <cstdint>

enum class FooEnum: std::uint64_t {};

int main()
{
    FooEnum myEnum;
    uint64_t* intPointer = (std::underlying_type<FooEnum>::type*)&myEnum;
}
Howard Hinnant
  • 179,402
  • 46
  • 391
  • 527
  • So close indeed. Apparently an appropriate name was chosen. :) – Kyle Feb 18 '12 at 22:08
  • I dont get it. If `intPointer`is declared to be `uint64_t*`, then why don't you simply use it in the cast expression as well? Why bother using `std::underlying_type::type*` at all? (Also, C-style cast is bad, is another topic). – Nawaz Sep 13 '18 at 16:40
9

Both Visual C++ 10.0 and MinGW g++ 4.6.1 lack std::underlying_type, but both accept this code:

template< class TpEnum >
struct UnderlyingType
{
    typedef typename conditional<
        TpEnum( -1 ) < TpEnum( 0 ),
        typename make_signed< TpEnum >::type,
        typename make_unsigned< TpEnum >::type
        >::type T;
};
Cheers and hth. - Alf
  • 135,616
  • 15
  • 192
  • 304
  • @Cheersandhth.-Alf @Grizzly: However, casts `TpEnum(-1)` and `TpEnum(0)` *may* have UB: 5.2.9 "Static cast", item 10 says " A value of integral or enumeration type can be explicitly converted to an enumeration type. The value is unchanged if the original value is within the range of the enumeration values (7.2). Otherwise, the resulting value is unspecified (and might not be in that range)." – Joker_vD Feb 28 '14 at 07:56
  • 1
    @Joker_vD: unspecified is not UB. – Cheers and hth. - Alf Nov 28 '14 at 03:38
2

Here is another approach for when underlying_type is not present. This method doesn't attempt to detect the signed-ness of the enum, just give you a type of the same size, which is more than enough for a lot of situations.

template<int>
class TIntegerForSize
{
    typedef void type;
};

template<>
struct TIntegerForSize<1>
{
    typedef uint8_t type;
};

template<>
struct TIntegerForSize<2>
{
    typedef uint16_t type;
};

template<>
struct TIntegerForSize<4>
{
    typedef uint32_t type;
};

template<>
struct TIntegerForSize<8>
{
    typedef uint64_t type;
};

template<typename T>
struct TIntegerForEnum
{
    typedef typename TIntegerForSize<sizeof(T)>::type type;
};

Usage:

enum EFoo {Alpha, Beta};
EFoo f = Alpha;
TIntegerForEnum<EFoo>::type i = f;
TIntegerForEnum<decltype(f)>::type j = f;
Zoner
  • 586
  • 5
  • 5