有没有办法将enum class
字段转换为基础类型?我认为这将是自动的,但显然不是。
enum class my_fields : unsigned { field = 1 };
unsigned a = my_fields::field;
GCC 拒绝了该分配。error: cannot convert 'my_fields' to 'unsigned int' in assignment
.
我认为您可以使用std::underlying_type来了解基础类型,然后使用强制转换:
#include <type_traits> //for std::underlying_type
typedef std::underlying_type<my_fields>::type utype;
utype a = static_cast<utype>(my_fields::field);
有了这个,您不必假设底层类型,或者您不必在enum class
类似的定义中提及它enum class my_fields : int { .... }
。
您甚至可以编写一个通用转换函数,该函数应该能够将任何 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
还要注意一个事实,分号应该在枚举定义中的闭合花括号之后,而不是之前。
使用 C++23,您最终将获得一个库函数:
它已经在 GCC 11、Clang 13 和 MSVC 19.30(又名 2022 17.0)的标准库中实现。
在您能够使用 C++23 之前,我建议您(重新)将任何自定义实现命名为to_underlying
并将其放置在一个#if !defined(__cpp_lib_to_underlying)
#endif
块之间,该块是关联的功能测试宏。这样,当 C++23 可供您使用时,您可以在将来某个时候简单地放弃代码。
正如其他人指出的那样,没有隐式强制转换,但您可以使用显式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);
}
使用代码
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
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;
}