#include <type_traits>
enum class MyEnum
{
Hello
};
template <typename T, typename Enable = void>
class MyClass
{
public:
MyClass(T obj) : e(obj)
{}
private:
T e;
};
template <typename T>
class MyClass <T,
typename std::enable_if< std::is_enum<T>::value
&& std::is_same<typename std::underlying_type<T>::type,
int>::value>::type >
{
public:
MyClass(T obj) : e(obj)
{}
private:
T e;
};
int main()
{
MyClass<MyEnum> c(MyEnum::Hello);
MyClass<int> c1(1); //does not compile due to std::underlying_type
return 0;
}
Я хотел бы иметь возможность специализировать MyClass для перечислений базового типа int. Я не могу сделать это с помощью std :: under_type, потому что он специфичен только для перечислений. Есть идеи, как я могу продолжить?





Достаточно простое решение этой причуды - изолировать std::underlying_type за вашей собственной дружественной к SFINAE чертой:
template <class T, class = void>
struct underlying_type {};
template <class T>
struct underlying_type<
T,
typename std::enable_if<std::is_enum<T>::value>::type
> {
using type = typename std::underlying_type<T>::type;
};
Тогда ваша специализация может быть записана как:
template <typename T>
class MyClass<
T,
typename std::enable_if<std::is_same<
typename underlying_type<T>::type,
int
>::value>::type
> {
// ...
};
С одним дополнительным (отложенным) обращением к conditional<..>::type::type:
template <typename T> struct Identity { using type = T; };
template <typename E>
class MyClass <E,
typename std::enable_if<
std::is_enum<E>::value
&& std::is_same<
int,
typename std::conditional<
std::is_enum<E>::value,
std::underlying_type<E>,
Identity<E>
>::type::type
>::value
>::type
>
{
// ...
};
Мы действительно делаем std::underlying_type<E>::type только для enum.
Я был удивлен, но действительно: «Если
Tявляется полным перечислимым типом, предоставляет тип определения типа члена, который именует базовыйtypeT. В противном случае поведение не определено».. Ну, это отстой.