Can an enum class be converted to the underlying type?

Is there a way to convert an enum class field to the underlying type? I thought this would be automatic, but apparently not.

enum class my_fields : unsigned { field = 1 };


unsigned a = my_fields::field;

That assignment is being rejected by GCC. error: cannot convert 'my_fields' to 'unsigned int' in assignment.

58253 次浏览

我认为您可以使用 底层 _ type来了解基础类型,然后使用强制转换:

#include <type_traits> //for std::underlying_type


typedef std::underlying_type<my_fields>::type utype;


utype a = static_cast<utype>(my_fields::field);

With this, you don't have to 假设 the underlying type, or you don't have to mention it in the definition of the enum class like enum class my_fields : int { .... } or so.

您甚至可以编写一个 一般转换函数,该函数应该能够将 任何 enum class转换为其底层的 积分类型:

template<typename E>
constexpr auto to_integral(E e) -> typename std::underlying_type<E>::type
{
return static_cast<typename std::underlying_type<E>::type>(e);
}

然后使用它:

auto value = to_integral(my_fields::field);


auto redValue = to_integral(Color::Red);//where Color is an enum class!

因为函数被声明为 constexpr,所以你可以在需要常数表达式的地方使用它:

int a[to_integral(my_fields::field)]; //declaring an array


std::array<int, to_integral(my_fields::field)> b; //better!

您不能将其转换为 毫无疑问,但可以进行显式强制转换:

enum class my_fields : unsigned { field = 1 };


// ...


unsigned x = my_fields::field; // ERROR!
unsigned x = static_cast<unsigned>(my_fields::field); // OK

还要注意一个事实,在枚举的定义中,分号应该是 之后,而不是在枚举之前。

我发现下面的函数 underlying_cast在必须正确序列化枚举值时很有用。

namespace util
{


namespace detail
{
template <typename E>
using UnderlyingType = typename std::underlying_type<E>::type;


template <typename E>
using EnumTypesOnly = typename std::enable_if<std::is_enum<E>::value, E>::type;


}   // namespace util.detail




template <typename E, typename = detail::EnumTypesOnly<E>>
constexpr detail::UnderlyingType<E> underlying_cast(E e) {
return static_cast<detail::UnderlyingType<E>>(e);
}


}   // namespace util


enum SomeEnum : uint16_t { A, B };


void write(SomeEnum /*e*/) {
std::cout << "SomeEnum!\n";
}


void write(uint16_t /*v*/) {
std::cout << "uint16_t!\n";
}


int main(int argc, char* argv[]) {
SomeEnum e = B;
write(util::underlying_cast(e));
return 0;
}

正如其他人指出的那样,没有隐式强制转换,但是可以使用显式的 static_cast。我在代码中使用下列助手函数来转换枚举类型及其基础类。

    template<typename EnumType>
constexpr inline decltype(auto) getIntegralEnumValue(EnumType enumValue)
{
static_assert(std::is_enum<EnumType>::value,"Enum type required");
using EnumValueType = std::underlying_type_t<EnumType>;
return static_cast<EnumValueType>(enumValue);
}


template<typename EnumType,typename IntegralType>
constexpr inline EnumType toEnum(IntegralType value)
{
static_assert(std::is_enum<EnumType>::value,"Enum type required");
static_assert(std::is_integral<IntegralType>::value, "Integer required");
return static_cast<EnumType>(value);
}


template<typename EnumType,typename UnaryFunction>
constexpr inline void setIntegralEnumValue(EnumType& enumValue, UnaryFunction integralWritingFunction)
{
// Since using reinterpret_cast on reference to underlying enum type is UB must declare underlying type value and write to it and then cast it to enum type
// See discussion on https://stackoverflow.com/questions/19476818/is-it-safe-to-reinterpret-cast-an-enum-class-variable-to-a-reference-of-the-unde


static_assert(std::is_enum<EnumType>::value,"Enum type required");


auto enumIntegralValue = getIntegralEnumValue(enumValue);
integralWritingFunction(enumIntegralValue);
enumValue = toEnum<EnumType>(enumIntegralValue);
}

Usage code

enum class MyEnum {
first = 1,
second
};


MyEnum myEnum = MyEnum::first;
std::cout << getIntegralEnumValue(myEnum); // prints 1


MyEnum convertedEnum = toEnum(1);


setIntegralEnumValue(convertedEnum,[](auto& integralValue) { ++integralValue; });
std::cout << getIntegralEnumValue(convertedEnum); // prints 2

使用 C + + 23,你最终会得到一个这样的库函数:

到 _ 潜在的

它已经在 GCC 11、 Clang 13和 MSVC 19.30(又名202217.0)的标准库中实现。

在您能够使用 C + + 23之前,我建议您(重新)为 to_underlying命名任何自定义实现,并将其放置在 #if !defined(__cpp_lib_to_underlying) #endif块之间,#if !defined(__cpp_lib_to_underlying) #endif块是相关的特性测试宏。通过这种方式,您可以在将来 C + + 23可用的某个时候丢弃这些代码。