Loïc Février
Loïc Février

Reputation: 7750

std::enable_if : parameter vs template parameter

I'm building some input checker that needs to have specific functions for integer and/or double (for example 'isPrime' should only be available for integers).

If I'm using enable_if as a parameter it's working perfectly :

template <class T>
class check
{
public:
   template< class U = T>
   inline static U readVal(typename std::enable_if<std::is_same<U, int>::value >::type* = 0)
   {
      return BuffCheck.getInt();
   }

   template< class U = T>
   inline static U readVal(typename std::enable_if<std::is_same<U, double>::value >::type* = 0)
   {
      return BuffCheck.getDouble();
   }   
};

but if I'm using it as a template paramater (as demonstrated on http://en.cppreference.com/w/cpp/types/enable_if )

template <class T>
class check
{
public:
   template< class U = T, class = typename std::enable_if<std::is_same<U, int>::value>::type >
   inline static U readVal()
   {
      return BuffCheck.getInt();
   }

   template< class U = T, class = typename std::enable_if<std::is_same<U, double>::value>::type >
   inline static U readVal()
   {
      return BuffCheck.getDouble();
   }
};

then I have the following error :

error: ‘template<class T> template<class U, class> static U check::readVal()’ cannot be overloaded
error: with ‘template<class T> template<class U, class> static U check::readVal()’

I can't figure out what is wrong in the second version.

Upvotes: 38

Views: 18243

Answers (3)

JVApen
JVApen

Reputation: 11317

I know this question is about std::enable_if, however, I like to provide an alternative solution to solve the same problem without enable_if. It does require C++17

template <class T>
class check
{
public:
   inline static T readVal()
   {
        if constexpr (std::is_same_v<T, int>)
             return BuffCheck.getInt();
        else if constexpr (std::is_same_v<T, double>)
             return BuffCheck.getDouble();
   }   
};

This code looks more as if you would write it at runtime. All branches have to be syntactic correct, however the semantics don't have to be. In this case, if T is int, the getDouble ain't causing compilation errors (or warnings) as it doesn't get checked/used by the compiler.

If the return type of the function would be to complex to mention, you can always use auto as the return type.

Upvotes: 7

Johannes Schaub - litb
Johannes Schaub - litb

Reputation: 507015

Default template arguments are not part of the signature of a template (so both definitions try to define the same template twice). Their parameter types are part of the signature, however. So you can do

template <class T>
class check
{
public:
   template< class U = T, 
             typename std::enable_if<std::is_same<U, int>::value, int>::type = 0>
   inline static U readVal()
   {
      return BuffCheck.getInt();
   }

   template< class U = T, 
             typename std::enable_if<std::is_same<U, double>::value, int>::type = 0>
   inline static U readVal()
   {
      return BuffCheck.getDouble();
   }
};

Upvotes: 46

mfontanini
mfontanini

Reputation: 21900

The problem is that the compiler sees 2 overloads of the same method, both which contain the same arguments(none, in this case) and the same return value. You can't provide such definition. The cleanest way to do this is to use SFINAE on the function's return value:

template <class T>
class check
{
public:
   template< class U = T>
   static typename std::enable_if<std::is_same<U, int>::value, U>::type readVal()
   {
      return BuffCheck.getInt();
   }

   template< class U = T>
   static typename std::enable_if<std::is_same<U, double>::value, U>::type readVal()
   {
      return BuffCheck.getDouble();
   }
};

That way, you're providing 2 different overloads. One returns an int, the other one returns a double, and only one can be instantiated using a certain T.

Upvotes: 10

Related Questions