Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Conversion" from type to same type is causing error

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

like image 345
Adrian Avatar asked Sep 08 '15 14:09

Adrian


2 Answers

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

like image 118
NathanOliver Avatar answered Nov 14 '22 20:11

NathanOliver


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

like image 23
Piotr Skotnicki Avatar answered Nov 14 '22 20:11

Piotr Skotnicki