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
istrue
and - if
T
is any other type,is_scoped_enum<T>::value
is false
Is there a type trait, or is it possible to write a type trait is_scoped_enum<T>
such that:
T
is a scoped enumeration, is_scoped_enum<T>::value
is true
andT
is any other type, is_scoped_enum<T>::value
is falseI 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> {};
std::underlying_type<T>
instead of int
. An enum class
in C++11 can base on something not convertible to int
. –
Grainfield int
? –
Laclair is_convertible
doing implicit conversion.) –
Grainfield ::value
is true if T
is an unscoped enumeration. :-) –
Laclair 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 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 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);
}
© 2022 - 2024 — McMap. All rights reserved.
enum class
is called in C++11, yes. – LaclairCXXREFLECT_GENERATE_SCOPED_ENUM_OPERATORS
) Unfortunately, Visual C++ does not supportconstexpr
, 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 aflags<T>
class type to wrap the enumeration. – Laclair