Is it possible to determine if a type is a scoped enumeration type?
Asked Answered
L

2

24

Is there a type trait, or is it possible to write a type trait is_scoped_enum<T> such that:

  • if T is a scoped enumeration, is_scoped_enum<T>::value is true and
  • if T is any other type, is_scoped_enum<T>::value is false
Laclair answered 23/5, 2012 at 17:15 Comment(7)
By scoped enum, you meant C++11's enum?Rashid
@Nawaz: A scoped enum is what an enum class is called in C++11, yes.Laclair
Just out of curiosity, what practical applications are there for this one?Elisavetpol
@Xeo: I am overloading the bitwise operators for a set of scoped enumeration types.Laclair
bitbucket.org/martinhofernandes/wheels/src/353fc67489dc/include/…Caerleon
@R.MartinhoFernandes: Ha; that looks familiar. :-) cxxreflect.codeplex.com/SourceControl/changeset/view/… (search for CXXREFLECT_GENERATE_SCOPED_ENUM_OPERATORS) Unfortunately, Visual C++ does not support constexpr, which makes scoped enums quite painful to use for flags, but at least with the operators it's better. I like your traits usage, though. For flags, I've been using a flags<T> class type to wrap the enumeration.Laclair
With c++23 this will be in the standard. If you can use c++17 or c++20 you can implement it in a shorter way than in R. Martinho Fernandes answer. cppreference has an example implementation for c++20: en.cppreference.com/w/cpp/types/is_scoped_enumStaffordshire
C
35

I think testing if it is an enum and not implicitly convertible to the underlying type should do the trick.

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> {};
Caerleon answered 23/5, 2012 at 17:18 Comment(7)
Better use std::underlying_type<T> instead of int. An enum class in C++11 can base on something not convertible to int.Grainfield
@KennyTM: What type? C++11 §7.2/2 states "The enum-base shall name an integral type;" is there an integral type not convertible to int?Laclair
@JamesMcNellis: You're correct. Sorry for confusion. (I was thinking of is_convertible doing implicit conversion.)Grainfield
This is a good solution, except that with the latest edit, the logic is now backwards: ::value is true if T is an unscoped enumeration. :-)Laclair
and one more correction, it should be !std::is_convertible<...>::valueTuscany
I would like to add (because I've had this issue): This trait could also be implemented as one using statement (template alias) as suggested here, but then std::underlying_type will be evaluated for all types, and some compilers generate an error when using underlying_type with non-enum types (no SFINAE). So this solution is probably the best one.Kinser
@Kinser It's worse than the possibility of getting an error or not: using std::underlying_type with a non-enum type is undefined behaviour, which requires no diagnostic, so the compiler need not report the misuse, which then gives your program UB. I didn't look at common implementations but presume the Standard allows UB to facilitate easier implementation in valid use cases.Gymnasiast
L
0

C++23 will be providing is_scoped_enum which can be used once it gets implemented. See this link for documentation: is_scoped_enum. I don't think clang supports this yet (see clang status for the latest info of what features are supported).

For now I am using a slightly simplified version of the answer above (using _v and _t) as well as an implementation of 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);
}
Linton answered 2/2, 2022 at 18:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.