Info
-
Did you know that C++23 added
is_scoped_enum
type trait to detect whether an enum is scoped?
Example
enum class scoped_enum { };
static_assert(std::is_scoped_enum<scoped_enum>{});
enum class scoped_enum_type : int { };
static_assert(std::is_scoped_enum<scoped_enum_type>{});
enum not_scoped_enum { };
static_assert(not std::is_scoped_enum<not_scoped_enum>{});
enum not_scoped_enum_type : int { };
static_assert(not std::is_scoped_enum<not_scoped_enum_type>{});
Puzzle
- Can you implement
scoped_enum
andany_enum
concepts to constrainfoo
overloads accordingly?
/*TODO - scoped_num, any_enum*/
using std::literals::string_view_literals::operator""sv;
constexpr auto foo(scoped_enum auto...) { return "scoped enums"sv; }
constexpr auto foo(any_enum auto...) { return "any enums"sv; }
constexpr auto foo(auto...) { return "..."sv; }
enum E { V1 };
enum ET : int { V2 };
enum class SEC : int { V3 };
enum struct SES : int { V4 };
enum : int { V5 };
static_assert("scoped enums"sv == foo(SEC{}));
static_assert("scoped enums"sv == foo(SEC::V3));
static_assert("scoped enums"sv == foo(SEC::V3, SES::V4));
static_assert("scoped enums"sv == foo(SEC::V3, SES::V4, SES{}, SES{}));
static_assert("any enums"sv == foo(E{}));
static_assert("any enums"sv == foo(E{}, ET::V2));
static_assert("any enums"sv == foo(V5, ET::V2, E{}));
static_assert("any enums"sv == foo(E{}, E::V1, V1, V5));
static_assert("..."sv == foo(0));
static_assert("..."sv == foo(0, V5));
static_assert("..."sv == foo(0, V1, E::V1, SEC::V3, SES::V4));
Solutions
void call_with_integer(int x) {};
template <typename T>
concept scoped_enum = std::is_enum_v<T> and not requires { call_with_integer(T{}); };
template <typename T>
concept any_enum = std::is_enum_v<T> and not scoped_enum<T>;
namespace detail {
template<typename T>
concept enumeration = std::is_enum_v<T>;
template<typename T>
concept integer_convertible = std::convertible_to<T, int>;
}
template<typename TEnum>
concept scoped_enum = detail::enumeration<TEnum> and not detail::integer_convertible<TEnum>;
template<typename TEnum>
concept any_enum = detail::enumeration<TEnum> and detail::integer_convertible<TEnum>;
template <typename... Ts>
concept any_enum = (std::is_enum_v<Ts> and ...);
template <typename... Ts>
concept scoped_enum = any_enum<Ts...> and
(not std::is_convertible_v<Ts, std::underlying_type_t<Ts>> and ...);
template <typename... Ts>
constexpr auto foo(Ts...) requires scoped_enum<Ts...> { return "scoped enums"sv; }
template <typename... Ts>
constexpr auto foo(Ts...) requires any_enum<Ts...> { return "any enums"sv; }
constexpr auto foo(auto...) { return "..."sv; }
template<class T> using is_scoped_enum =
std::bool_constant<std::is_enum_v<T> and not std::is_convertible_v<T, std::underlying_type_t<T>>>;
template<class T> concept scoped_enum = is_scoped_enum<T>::value;
template<class T> concept any_enum = std::is_enum<T>::value and not is_scoped_enum<T>::value;
template <typename T> concept any_enum = std::is_enum_v<T> && std::is_convertible_v<T, std::underlying_type_t<T>>;
template <typename T> concept scoped_enum = std::is_enum_v<T> && !std::is_convertible_v<T, std::underlying_type_t<T>>;
template<typename T>
concept scoped_enum = std::is_enum_v<T> and not std::is_convertible_v<T, std::underlying_type_t<T>>;
template<typename T>
concept any_enum = std::is_enum_v<T> and std::is_convertible_v<T, std::underlying_type_t<T>>;