How to automatically convert strongly typed enum into int?

C++C++11Strongly Typed-Enum

C++ Problem Overview


#include <iostream>

struct a {
  enum LOCAL_A { A1, A2 };
};
enum class b { B1, B2 };

int foo(int input) { return input; }

int main(void) {
  std::cout << foo(a::A1) << std::endl;
  std::cout << foo(static_cast<int>(b::B2)) << std::endl;
}

The a::LOCAL_A is what the strongly typed enum is trying to achieve, but there is a small difference : normal enums can be converted into integer type, while strongly typed enums can not do it without a cast.

So, is there a way to convert a strongly typed enum value into an integer type without a cast? If yes, how?

C++ Solutions


Solution 1 - C++

As others have said, you can't have an implicit conversion, and that's by-design.

If you want you can avoid the need to specify the underlying type in the cast.

template <typename E>
constexpr typename std::underlying_type<E>::type to_underlying(E e) noexcept {
    return static_cast<typename std::underlying_type<E>::type>(e);
}

std::cout << foo(to_underlying(b::B2)) << std::endl;

Solution 2 - C++

Strongly typed enums aiming to solve multiple problems and not only scoping problem as you mentioned in your question:

  1. Provide type safety, thus eliminating implicit conversion to integer by integral promotion.
  2. Specify underlying types.
  3. Provide strong scoping.

Thus, it is impossible to implicitly convert a strongly typed enum to integers, or even its underlying type - that's the idea. So you have to use static_cast to make conversion explicit.

If your only problem is scoping and you really want to have implicit promotion to integers, then you better off using not strongly typed enum with the scope of the structure it is declared in.

Solution 3 - C++

A C++14 version of the answer provided by R. Martinho Fernandes would be:

#include <type_traits>

template <typename E>
constexpr auto to_underlying(E e) noexcept
{
    return static_cast<std::underlying_type_t<E>>(e);
}

As with the previous answer, this will work with any kind of enum and underlying type. I have added the noexcept keyword as it will never throw an exception.


Update
This also appears in Effective Modern C++ by Scott Meyers. See item 10 (it is detailed in the final pages of the item within my copy of the book).


A C++23 version would be to use the std::to_underlying function:

#include <utility>

std::cout << std::to_underlying(b::B2) << std::endl;

...or if the underlying type could be a 1 byte type:

std::cout << +(std::to_underlying(b::B2)) << std::endl;

Solution 4 - C++

The reason for the absence of implicit conversion (by design) was given in other answers.

I personally use unary operator+ for the conversion from enum classes to their underlying type:

template <typename T>
constexpr auto operator+(T e) noexcept
	-> std::enable_if_t<std::is_enum<T>::value, std::underlying_type_t<T>>
{
	return static_cast<std::underlying_type_t<T>>(e);
}

Which gives quite little "typing overhead":

std::cout << foo(+b::B2) << std::endl;

Where I actually use a macro to create enums and the operator functions in one shot.

#define UNSIGNED_ENUM_CLASS(name, ...) enum class name : unsigned { __VA_ARGS__ };\
inline constexpr unsigned operator+ (name const val) { return static_cast<unsigned>(val); }

Solution 5 - C++

Short answer is you can't as above posts point out. But for my case, I simply didn't want to clutter the namespace but still have implicit conversions, so I just did:

#include <iostream>

using namespace std;

namespace Foo {
   enum Foo { bar, baz };
}

int main() {
   cout << Foo::bar << endl; // 0
   cout << Foo::baz << endl; // 1
   return 0;
}

The namespacing sort of adds a layer of type-safety while I don't have to static cast any enum values to the underlying type.

Solution 6 - C++

No. There is no natural way.

In fact, one of the motivations behind having strongly typed enum class in C++11 is to prevent their silent conversion to int.

Solution 7 - C++

#include <cstdlib>
#include <cstdio>
#include <cstdint>

#include <type_traits>

namespace utils
{
	
namespace details
{
	
template< typename E >
using enable_enum_t = typename std::enable_if< std::is_enum<E>::value, 
                                               typename std::underlying_type<E>::type 
                                             >::type;
	
}	// namespace details
	
	
template< typename E >
constexpr inline details::enable_enum_t<E> underlying_value( E e )noexcept
{
	return static_cast< typename std::underlying_type<E>::type >( e );
}	
	

template< typename E , typename T>
constexpr inline typename std::enable_if< std::is_enum<E>::value &&
                                          std::is_integral<T>::value, E
                                         >::type 
 to_enum( T value ) noexcept 
 {
	 return static_cast<E>( value );
 }
	
} // namespace utils




int main()
{
	enum class E{ a = 1, b = 3, c = 5 };
	
	constexpr auto a = utils::underlying_value(E::a);
	constexpr E    b = utils::to_enum<E>(5);
	constexpr auto bv = utils::underlying_value(b);
	
	printf("a = %d, b = %d", a,bv);
	return 0;
}

Solution 8 - C++

Hope this helps you or someone else

enum class EnumClass : int //set size for enum
{
	Zero, One, Two, Three, Four
};

union Union //This will allow us to convert
{
    EnumClass ec;
    int i;
};

int main()
{
using namespace std;

//convert from strongly typed enum to int

Union un2;
un2.ec = EnumClass::Three;

cout << "un2.i = " << un2.i << endl;

//convert from int to strongly typed enum
Union un;
un.i = 0; 

if(un.ec == EnumClass::Zero) cout << "True" << endl;

return 0;
}

Solution 9 - C++

This seems impossible with the native enum class, but probably you can mock a enum class with a class:

In this case,

enum class b
{
    B1,
    B2
};

would be equivalent to:

class b {
 private:
  int underlying;
 public:
  static constexpr int B1 = 0;
  static constexpr int B2 = 1;
  b(int v) : underlying(v) {}
  operator int() {
      return underlying;
  }
};

This is mostly equivalent to the original enum class. You can directly return b::B1 for in a function with return type b. You can do switch case with it, etc.

And in the spirit of this example you can use templates (possibly together with other things) to generalize and mock any possible object defined by the enum class syntax.

Solution 10 - C++

As many said, there is no way to automatically convert without adding overheads and too much complexity, but you can reduce your typing a bit and make it look better by using lambdas if some cast will be used a bit much in a scenario. That would add a bit of function overhead call, but will make code more readable compared to long static_cast strings as can be seen below. This may not be useful project wide, but only class wide.

#include <bitset>
#include <vector>

enum class Flags { ......, Total };
std::bitset<static_cast<unsigned int>(Total)> MaskVar;
std::vector<Flags> NewFlags;

-----------
auto scui = [](Flags a){return static_cast<unsigned int>(a); };

for (auto const& it : NewFlags)
{
	switch (it)
	{
	case Flags::Horizontal:
		MaskVar.set(scui(Flags::Horizontal));
		MaskVar.reset(scui(Flags::Vertical)); break;
	case Flags::Vertical:
		MaskVar.set(scui(Flags::Vertical));
		MaskVar.reset(scui(Flags::Horizontal)); break;
	
   case Flags::LongText:
		MaskVar.set(scui(Flags::LongText));
		MaskVar.reset(scui(Flags::ShorTText)); break;
	case Flags::ShorTText:
		MaskVar.set(scui(Flags::ShorTText));
		MaskVar.reset(scui(Flags::LongText)); break;
	
    case Flags::ShowHeading:
		MaskVar.set(scui(Flags::ShowHeading));
		MaskVar.reset(scui(Flags::NoShowHeading)); break;
	case Flags::NoShowHeading:
		MaskVar.set(scui(Flags::NoShowHeading));
		MaskVar.reset(scui(Flags::ShowHeading)); break;
	
    default:
		break;
	}
}

Solution 11 - C++

The C++ committee took one step forward (scoping enums out of global namespace) and fifty steps back (no enum type decay to integer). Sadly, enum class is simply not usable if you need the value of the enum in any non-symbolic way.

The best solution is to not use it at all, and instead scope the enum yourself using a namespace or a struct. For this purpose, they are interchangable. You will need to type a little extra when refering to the enum type itself, but that will likely not be often.

struct TextureUploadFormat {
	enum Type : uint32 {
		r,
		rg,
		rgb,
		rgba,
		__count
	};
};

// must use ::Type, which is the extra typing with this method; beats all the static_cast<>()
uint32 getFormatStride(TextureUploadFormat::Type format){
    const uint32 formatStride[TextureUploadFormat::__count] = {
        1,
        2,
        3,
        4
	};
    return formatStride[format]; // decays without complaint
}

Solution 12 - C++

Summary

From here (emphasis added): https://en.cppreference.com/w/cpp/language/enum --> under the section "Scoped enumerations":

> There are no implicit conversions from the values of a scoped enumerator [AKA: "strong enum"] to integral types, although static_cast may be used to obtain the numeric value of the enumerator.

Going further

In C++ there are two types of enums:

  1. "regular", "weak", "weakly typed", or "C-style" enums, and
  2. "scoped", "strong", "strongly typed", "enum class", or "C++-style" enums.

"Scoped" enums, or "strong" enums, give two additional "features" beyond what "regular" enums give you. Scoped enums:

  1. don't allow implicit casting from the enum type to an integer type (so you can't do what you want to do implicitly!), and
  2. they "scope" the enum so that you have to access the enum through its enum type name.
1. Example of an enum class (available only in C++):
// enum class (AKA: "strong" or "scoped" enum)
enum class my_enum
{
    A = 0,
    B,
    C,
};

my_enum e = my_enum::A; // scoped through `my_enum::`
e = my_enum::B;

// NOT ALLOWED!:
//   error: cannot convert ‘my_enum’ to ‘int’ in initialization
// int i = e; 

// But this works fine:
int i = static_cast<int>(e);

The first "feature" may actually be something you don't want, in which case you just need to use a regular C-style enum instead! And the nice thing is: you can still "scope" or "namespace" the enum, as has been done in C for decades, by simply prepending its name with the enum type name, like this:

2. Example of a regular enum (available in both C and C++):
// regular enum (AKA: "weak" or "C-style" enum)
enum my_enum
{
    // C-style-scoped through the `MY_ENUM_` prefix
    MY_ENUM_A = 0,
    MY_ENUM_B,
    MY_ENUM_C,
};

my_enum e = MY_ENUM_A; // scoped through `MY_ENUM_`
e = MY_ENUM_B;

// This works fine!
int i = e;

Notice you still get the benefit of "scoping" simply by adding the MY_ENUM_ "scope" to the front of each enum!

3. Both regular enums and enum classes together:

Test the code here: https://onlinegdb.com/BkWGqlqz_.

main.cpp:

#include <iostream>
#include <stdio.h>

// enum class (AKA: "strong" or "scoped" enum [available only in C++, not C])
enum class my_enum
{
    A = 0,
    B,
    C,
};

// regular enum (AKA: "weak" or "C-style" enum [available in BOTH C and C++])
enum my_enum2
{
    MY_ENUM_A = 0,
    MY_ENUM_B,
    MY_ENUM_C,
};


int main()
{
    printf("Hello World\n");

    // 1) scoped enum

    my_enum e = my_enum::A; // scoped through `my_enum::`
    e = my_enum::B;
    
    // IMPLICIT CASTING TO INT IS NOT ALLOWED!
    // int i = e; // "error: cannot convert ‘my_enum’ to ‘int’ in initialization"
    // But this explicit C++-style cast works fine:
    int i1 = static_cast<int>(e); 
    // This explicit C-style cast works fine too, and is easier to read
    int i2 = (int)e;
    
    
    // 2) regular enum 
    
    my_enum2 e2 = MY_ENUM_A; // scoped through `MY_ENUM_`
    e2 = MY_ENUM_B;
    
    // This implicit cast works fine / IS allowed on C-style enums!
    int i3 = e2;
    // These explicit casts are also fine, but explicit casting is NOT 
    // required for regular enums.
    int i4 = static_cast<int>(e2); // explicit C++-style cast 
    int i5 = (int)e2;              // explicit C-style cast 

    return 0;
}

4. How to iterate over enums:

  1. *****[my answer] Full examples of how to iterate over both 1. weakly-typed C-style and 2. scoped, strongly-typed C++ enum class enums: How can I iterate over an enum?
  2. [my Q&A] What are commonly-used ways to iterate over an enum class in C++?

Solution 13 - C++

An extension to the answers from R. Martinho Fernandes and Class Skeleton: Their answers show how to use typename std::underlying_type<EnumType>::type or std::underlying_type_t<EnumType> to convert your enumeration value with a static_cast to a value of the underlying type. Compared to a static_cast to some specific integer type, like, static_cast<int> this has the benefit of being maintenance friendly, because when the underlying type changes, the code using std::underlying_type_t will automatically use the new type.

This, however, is sometimes not what you want: Assume you wanted to print out enumeration values directly, for example to std::cout, like in the following example:

enum class EnumType : int { Green, Blue, Yellow };
std::cout << static_cast<std::underlying_type_t<EnumType>>(EnumType::Green);

If you later change the underlying type to a character type, like, uint8_t, then the value of EnumType::Green will not be printed as a number, but as a character, which is most probably not what you want. Thus, you sometimes would rather convert the enumeration value into something like "underlying type, but with integer promotion where necessary".

It would be possible to apply the unary operator+ to the result of the cast to force integer promotion if necessary. However, you can also use std::common_type_t (also from header file <type_traits>) to do the following:

enum class EnumType : int { Green, Blue, Yellow };
std::cout << static_cast<std::common_type_t<int, std::underlying_type_t<EnumType>>>(EnumType::Green);

Preferrably you would wrap this expression in some helper template function:

template <class E>
constexpr std::common_type_t<int, std::underlying_type_t<E>>
enumToInteger(E e) {
    return static_cast<std::common_type_t<int, std::underlying_type_t<E>>>(e);
}

Which would then be more friendly to the eyes, be maintenance friendly with respect to changes to the underlying type, and without need for tricks with operator+:

std::cout << enumToInteger(EnumType::Green);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBЈовићView Question on Stackoverflow
Solution 1 - C++R. Martinho FernandesView Answer on Stackoverflow
Solution 2 - C++user405725View Answer on Stackoverflow
Solution 3 - C++Class SkeletonView Answer on Stackoverflow
Solution 4 - C++PixelchemistView Answer on Stackoverflow
Solution 5 - C++solstice333View Answer on Stackoverflow
Solution 6 - C++iammilindView Answer on Stackoverflow
Solution 7 - C++Khurshid NormuradovView Answer on Stackoverflow
Solution 8 - C++vis.15View Answer on Stackoverflow
Solution 9 - C++ColliotView Answer on Stackoverflow
Solution 10 - C++Atul KumarView Answer on Stackoverflow
Solution 11 - C++Anne QuinnView Answer on Stackoverflow
Solution 12 - C++Gabriel StaplesView Answer on Stackoverflow
Solution 13 - C++Dirk HerrmannView Answer on Stackoverflow