I have a template function where an enum type is converted to its underlying type which works fine, but I wrote an overload which should take an integral number and return itself and it give me an error that int is not an enumeration type. In my template, this should have been filtered out. What is wrong?
Here is the template code:
template <typename TT>
static constexpr auto get_value(TT t)
-> typename std::enable_if<!std::is_enum<TT>::value, TT>::type
{
return t;
}
template <typename TT>
static constexpr auto get_value(TT t)
-> typename std::enable_if<std::is_enum<TT>::value, typename std::underlying_type<TT>::type>::type
{
return (typename std::underlying_type<TT>::type)t;
}
Demo
std::underlying_type<TT>::type
is being evaluated in std::enable_if
even though std::is_enum<TT>::value
is false
as false
is not an error. Since a non enumeration type is being evaluated it is causing an error. If we move the SFINAE into the template parameters we can get the desired overloads and still return the correct type.
template <typename TT, typename std::enable_if<!std::is_enum<TT>::value, TT>::type* = nullptr>
static constexpr auto get_value(TT t) -> TT
{
return t;
}
template <typename TT, typename std::enable_if<std::is_enum<TT>::value>::type* = nullptr>
static constexpr auto get_value(TT t) -> typename std::underlying_type<TT>::type
{
return (typename std::underlying_type<TT>::type)t;
}
You can see it working in this Live Example
By attempting to instantiate std::underlying_type<T>
with T
that is not an enum type, you are violating a requirement that the Standard imposes on template parameter T
:
§ 20.10.7.6 [meta.trans.other]/Table 57:
Template | Condition | Comments
------------------------+---------------------------+-----------------------
template <class T> | T shall be an enumeration | The member typedef
struct underlying_type; | type (7.2) | type shall name
| | the underlying type
| | of T.
Here's an alternative approach if one doesn't like any additional template parameters:
template <typename TT>
static constexpr auto get_value(TT t)
-> typename std::enable_if<!std::is_enum<TT>::value, TT>::type
{
return t;
}
template <typename TT>
static constexpr auto get_value(TT t)
-> typename std::enable_if<std::is_enum<TT>::value
, std::underlying_type<TT>
>::type::type
{
return (typename std::underlying_type<TT>::type)t;
}
This way, the instantiation of std::underlying_type<TT>
is deferred until the condition in std::enable_if
evaluates to true
, because a nested type
definition is requested for what std::enable_if<B,T>::type
returns.
DEMO
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