I need to write a metaprogramming construct, that when given an enum type, it returns an underlying type of that enum, but when given an integer, it returns that integer.
For example:
enum Enum : short { VALUE1, VALUE2 };
int_type<long>::type // -> long
int_type<Enum>::type // -> short
I attempted this
template< typename Type >
struct int_type {
using type = typename std::enable_if< std::is_enum<Type>::value, typename std::underlying_type<Type>::type >::type;
};
template< typename Type >
struct int_type {
using type = typename std::enable_if< std::is_integral<Type>::value, Type >::type;
};
but it complains about redefinition of the struct.
I also tried this, but
template< typename Type >
struct int_type {
using type = typename std::enable_if< std::is_enum<Type>::value, typename std::underlying_type<Type>::type >::type;
using type = typename std::enable_if< std::is_integral<Type>::value, Type >::type;
};
but then it complains about redefinition of member type
.
This is where my metaprogramming skills end, can anyone help?
EDIT: I should have also mentioned, that our project is limited to C++11.
The problem with your attempts is that you're defining the same thing twice, and although part of it uses enable_if
that doesn't make the entire enclosing definition disabled when the enable_if
isn't true. i.e. you have two definitions of int_type::type
even when one (or both) of those definitions is invalid.
What you're looking for is std::conditional
:
template< typename Type >
struct int_type {
using when_enum
= std::underlying_type<Type>;
using when_integral
= std::enable_if<std::is_integral<Type>::value, Type>;
using type
= typename std::conditional< std::is_enum<Type>::value,
when_enum, when_integral
>::type::type;
};
Or in C++14 and later:
template< typename Type >
struct int_type {
using when_enum
= std::underlying_type<Type>;
using when_integral
= std::enable_if<std::is_integral<Type>::value, Type>;
using type
= typename std::conditional_t< std::is_enum<Type>::value,
when_enum, when_integral
>::type;
};
But I think I'd probably write it as an alias template:
template< typename Type >
using int_type = std::conditional_t<
std::is_enum<Type>::value,
std::underlying_type<Type>,
std::enable_if<std::is_integral<Type>::value, Type>>;
And maybe simplify it further and make int_type<T>
an alias for the actual type, so you don't need to say typename int_type<T>::type
:
template< typename Type >
using int_type = typename std::conditional_t<
std::is_enum<Type>::value,
std::underlying_type<Type>,
std::enable_if<std::is_integral<Type>::value, Type>>::type;
(N.B. In C++17 you can use std::is_enum_v<Type>
and std::is_integral_v
instead of saying is_xxx<Type>::value
.)
You can do it with partial specializations.
// primary template
template< typename Type, typename = void >
struct int_type {
};
// partial specialization for enum types
template< typename Type >
struct int_type <Type, typename std::enable_if< std::is_enum<Type>::value >::type > {
using type = typename std::underlying_type<Type>::type;
};
// partial specialization for integral types
template< typename Type>
struct int_type <Type, typename std::enable_if< std::is_integral<Type>::value >::type > {
using type = Type;
};
LIVE
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With