确定一个类型是否为作用域枚举类型是否有可能?

23

是否有一种类型特征或者可以编写一种类型特征is_scoped_enum<T>,使得:

  • 如果T是一个作用域枚举,则is_scoped_enum<T>::valuetrue,并且
  • 如果T是任何其他类型,则is_scoped_enum<T>::value为false

你是指 C++11 中的 scoped enum 枚举类型吗? - Nawaz
3
一个scoped enum在C++11中被称为enum class - James McNellis
2
@Xeo:我正在为一组作用域枚举类型重载位运算符。 - James McNellis
https://bitbucket.org/martinhofernandes/wheels/src/353fc67489dc/include/wheels/enums.h%2B%2B - R. Martinho Fernandes
使用C++23,这将成为标准。如果您可以使用C++17或C++20,则可以比R. Martinho Fernandes的答案更简短地实现它。cppreference为C++20提供了一个示例实现: https://en.cppreference.com/w/cpp/types/is_scoped_enum - Janek Beck
显示剩余2条评论
2个回答

35

我认为测试它是否是枚举类型并且不能隐式转换为基础类型就可以解决问题。

template <typename T, bool B = std::is_enum<T>::value>
struct is_scoped_enum : std::false_type {};

template <typename T>
struct is_scoped_enum<T, true>
: std::integral_constant<bool,
    !std::is_convertible<T, typename std::underlying_type<T>::type>::value> {};

2
最好使用std::underlying_type<T>而不是int。在C++11中,enum class可以基于某些不可转换为int的东西。 - kennytm
@KennyTM:是什么类型?C++11 §7.2/2规定:“_枚举基础类型_必须命名为整数类型;”是否存在一种不可转换为int的整数类型? - James McNellis
还有一个更正,应该是!std::is_convertible<...>::value - Gene Bushuyev
我想补充一下(因为我曾经遇到过这个问题):这个特性也可以通过一个using语句(模板别名)来实现,就像这里所建议的那样。但是,这样会对所有类型进行std::underlying_type的评估,而一些编译器在使用underlying_type与非枚举类型时会生成错误(没有SFINAE)。因此,这个解决方案可能是最好的。 - Excelcius
3
@Excelcius 更糟糕的是,使用std::underlying_type与非枚举类型是未定义的行为,这不需要诊断,因此编译器不必报告误用,这将导致您的程序出现UB。我没有查看常见实现,但推测标准允许UB以便在有效的用例中更容易实现。 - underscore_d
显示剩余2条评论

0

C++23将提供is_scoped_enum,一旦实现就可以使用。请参阅此链接以获取文档:is_scoped_enum。我认为clang目前还不支持这个功能(请参阅clang status以获取最新的支持功能信息)。

目前,我正在使用上面答案的稍微简化版本(使用_v和_t),以及is_underlying的实现:

// C++ 23 should include 'is_scoped_enum' and 'to_underlying' so the following
// code can be removed:

template<typename T, bool B = std::is_enum_v<T>>
struct is_scoped_enum : std::false_type {};

template<typename T>
struct is_scoped_enum<T, true>
  : std::integral_constant<
      bool, !std::is_convertible_v<T, std::underlying_type_t<T>>> {};

template<typename T>
inline constexpr bool is_scoped_enum_v = is_scoped_enum<T>::value;

template<typename T, std::enable_if_t<is_scoped_enum_v<T>, int> = 0>
[[nodiscard]] constexpr auto to_underlying(T x) noexcept {
  return static_cast<std::underlying_type_t<T>>(x);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接