Matteo Monti
Matteo Monti

Reputation: 8940

Execute different functions depending on template parameter disequation

This is definitely a trivial question, but I couldn't figure out how to do this.

I have a template function, say template <unsigned int N> void my_function(). Now, I have two different implementations for my_function, the first should be used if N is bigger than, say, 100, the other if N is smaller than that.

I tried to use SFINAE like this:

template <unsigned int N, typename = enable_if <N >= 100> :: type> my_function()
{
   // First implementation
}

template <unsigned int N, typename = enable_if <N < 100> :: type> my_function()
{
   // Second implementation
}

But that's declaring the same function two times. Then I tried doing something like

template <unsigned int N, bool = (N >= 100)> my_function();

And then implementing the two functions with the two different values of the boolean. No success, since it is a partial specialization.

Then I tried to wrap N as a struct parameter, and the bool in the function call, but it is specializing a member function before specializing the class, which cannot be done.

Is there a reasonable way to do this?

Upvotes: 15

Views: 6487

Answers (3)

Patrice Roy
Patrice Roy

Reputation: 101

If you don't like enable_if for some reason, you can always go for tag dispatch:

#include <type_traits>
class low {};
class high {};
template <int N, class T>
   void func(T, low)
   {
      // version for high N
   }
template <int N, class T>
   void func(T, high)
   {
      // version for low N
   }
template <int N, class T>
   void func(T val)
   {
      func<N>(val, std::conditional_t<(N>=100), high, low>{});
   }
int main()
{
   func<3>(3.14159); // low version
   func<256>("Yo"); // high version
}

In this case, we could restrict the tags to simple things like true_type and false_type, but in general this might be an alternative approach.

Upvotes: 5

vsoftco
vsoftco

Reputation: 56547

Try this instead:

#include <type_traits>
#include <iostream>

template <unsigned int N, typename std::enable_if <N >= 100> :: type* = nullptr> 
void my_function()
{
    std::cout << "N >= 100" << std::endl;
}

template <unsigned int N, typename std::enable_if <N < 100> :: type* = nullptr> 
void my_function()
{
   std::cout << "N < 100" << std::endl;
}

int main()
{
    my_function<42>();
    my_function<100>();
}

Template default parameters do not participate in the overload (and hence SFINAE does not apply). On the other hand, in the snippet above, the dependent template non-type parameter is on the left hand side of the assignment, so SFINAE kicks in.

Upvotes: 12

Jarod42
Jarod42

Reputation: 217235

You may use SFINAE on return type:

template <unsigned int N>
enable_if_t<(N >= 100)> my_function()
{
   // First implementation
}

template <unsigned int N>
enable_if_t<(N < 100)> my_function()
{
   // Second implementation
}

currently, you have only template <unsigned int N, typename T> with different default type for T.

For the partial specialization, you may forward to structure:

template <unsigned int N, bool = (N >= 100)>
struct my_function_impl;

template <unsigned int N>
struct my_function_impl<N, true>
{
    void operator () const { /* First implementation */}
};

template <unsigned int N>
struct my_function_impl<N, false>
{
    void operator () const { /* Second implementation */}
};

template <unsigned int N>
void my_function() { my_function_impl<N>{}(); }

Upvotes: 2

Related Questions