Search code examples
c++c++11sfinaeenable-if

Partial template function specialization with enable_if: make default implementation


Using C++11's enable_if I want to define several specialized implementations for a function (based on the type of the parameter, say) as well as a default implementation. What is the correct way to define it?

The following example does not work as intended since the "generic" implementation is called, whatever the type T.

#include <iostream>

template<typename T, typename Enable = void>
void dummy(T t)
{
  std::cout << "Generic: " << t << std::endl;
}


template<typename T, typename std::enable_if<std::is_integral<T>::value>::type>
void dummy(T t)
{
  std::cout << "Integral: " << t << std::endl;
}


template<typename T, typename std::enable_if<std::is_floating_point<T>::value>::type>
void dummy(T t)
{
  std::cout << "Floating point: " << t << std::endl;
}

int main() {
  dummy(5); // Print "Generic: 5"
  dummy(5.); // Print "Generic: 5"
}

One solution in my minimal example consists in explicitly declaring the "generic" implementation as not for integral nor floating point types, using

std::enable_if<!std::is_integral<T>::value && !std::is_floating_point<T>::value>::type

This is exactly what I want to avoid, since in my real use cases there are a lot of specialized implementations and I would like to avoid a very long (error prone!) condition for the default implementation.


Solution

  • Function cannot be partially specialized. I assume what you want to do is to prefer those overloads which contains explicit condition? One way to achieve that is by using variadic arguments ellipsis in declaration of the default function as the ellipsis function have lower priority in overload resolution order:

    #include <iostream>
    
    template<typename T>
    void dummy_impl(T t, ...)
    {
      std::cout << "Generic: " << t << std::endl;
    }
    
    
    template<typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
    void dummy_impl(T t, int)
    {
      std::cout << "Integral: " << t << std::endl;
    }
    
    
    template<typename T, typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
    void dummy_impl(T t, int)
    {
      std::cout << "Floating point: " << t << std::endl;
    }
    
    template <class T>
    void dummy(T t) {
       dummy_impl(t, int{});
    }
    
    int main() {
      dummy(5); 
      dummy(5.); 
      dummy("abc"); 
    }
    

    Output:

    Integral: 5
    Floating point: 5
    Generic: abc
    

    [live demo]

    Another option as @doublep mention in comment is by use of structure with implementation of your function and then partially specialize it.