c++templatesc++14overloading

Overloading function with multiple template definitions not possible?


I tried this:

template<typename P, typename = std::enable_if_t<std::is_arithmetic<P>::value>>
void f(std::vector<P>* a) {
    // body for arithmetic P
}

template<typename P, typename = std::enable_if_t<std::is_class<P>::value>>
void f(std::vector<P>* a) {
    // body for class P
}

I thought it would overload f as the conditions are mutually exclusive, but found that it doesn't compile:

function template has already been defined.

What to do instead, if I want the function body of f(std::vector<P>*) to depend on whether P is arithmetic?


Solution

  • The std::enable_if documentation on cppreference.com says:

    A common mistake is to declare two function templates that differ only in their default template arguments. This is illegal because default template arguments are not part of function template's signature, and declaring two different function templates with the same signature is illegal.

    The examples on that same page show a similar situation as yours, and solve it by changing the template on one of the overloads, while maintaining the same signature for the functions themselves:

    // #4, enabled via a template parameter
    template<class T,
             typename std::enable_if<
                 !std::is_trivially_destructible<T>{} &&
                 (std::is_class<T>{} || std::is_union<T>{}),
                int>::type = 0>
    void destroy(T* t)
    {
        std::cout << "destroying non-trivially destructible T\n";
        t->~T();
    }
    
    // #5, enabled via a template parameter
    template<class T,
        typename = std::enable_if_t<std::is_array<T>::value> >
    void destroy(T* t) // note, function signature is unmodified
    {
        for(std::size_t i = 0; i < std::extent<T>::value; ++i) {
            destroy((*t)[i]);
        }
    }
    /*
    template<class T,
        typename = std::enable_if_t<std::is_void<T>::value> >
    void destroy(T* t){} // error: has the same signature with #5
    */
    

    So, you can do something similar in your code:

    template<typename P, std::enable_if_t<std::is_arithmetic<P>::value, int> = 0>
    void f(std::vector<P>* a)
    {
        // body for arithmetic P
    }
    
    template<typename P, typename = std::enable_if_t<std::is_class<P>::value>>
    void f(std::vector<P>* a)
    {
        // body for class P
    }
    

    Live Demo