17
votes

In C++ if you want to partially specialize a single method in a template class you have to specialize the whole class (as stated for example in Template specialization of a single method from templated class with multiple template parameters)

This however becomes tiresome in bigger template classes with multiple template parameters, when each of them influences a single function. With N parameters you need to specialize the class 2^N times!

However, with the C++11 I think there might a more elegant solution, but I am not sure how to approach it. Perhaps somehow with enable_if? Any ideas?

5

5 Answers

13
votes

In addition to the inheritance-based solution proposed by Torsten, you could use std::enable_if and default function template parameters to enable/disable certain specializations of the function.

For example:

template<typename T>
struct comparer
{
    template<typename U = T ,
    typename std::enable_if<std::is_floating_point<U>::value>::type* = nullptr>
    bool operator()( U lhs , U rhs )
    {
        return /* floating-point precision aware comparison */;
    }

    template<typename U = T ,
    typename std::enable_if<!std::is_floating_point<U>::value>::type* = nullptr>
    bool operator()( U lhs , U rhs )
    {
        return lhs == rhs;
    } 
};

We take advantage of SFINAE to disable/enable the different "specializations" of the function depending on the template parameter. Because SFINAE can only depend on function parameters, not class parameters, we need an optional template parameter for the function, which takes the parameter of the class.

I prefer this solution over the inheritance based because:

  • It requires less typing. Less typing probably leads to less errors.
  • All specializations are written inside the class. This way to write the specializations holds all of the specializations inside the original class , and make the specializations look like function overloads, instead of tricky template based code.

But with compilers which have not implemented optional function template parameters (Like MSVC in VS2012) this solution does not work, and you should use the inheritance-based solution.

EDIT: You could ride over the non-implemented-default-function-template-parameters wrapping the template function with other function which delegates the work:

template<typename T>
struct foo
{
private:
    template<typename U>
    void f()
    {
        ...
    }

public:
    void g()
    {
        f<T>();
    }
};

Of course the compiler can easily inline g() throwing away the wrapping call, so there is no performance hit on this alternative.

4
votes

One solution would be to forward from the function, you want to overload to some implementation that depends on the classes template arguments:

template < typename T >
struct foo {
   void f();
};

template < typename T >
struct f_impl {
    static void impl()
    {
        // default implementation
    }
};

template <>
struct f_impl<int> {
    static void impl()
    {
         // special int implementation
    }
};

template < typename T >
void foo< T >::f()
{
    f_impl< T >::impl();
}

Or just use private functions, call them with the template parameter and overload them.

template < typename T >
class foo {
public:
    void f()
    {
        impl(T());
    }
private:
    template < typename G >
    void impl( const G& );
    void impl( int );
};

Or if it's really just one special situation with a very special type, just query for that type in the implementation.

4
votes

With enable_if:

#include <iostream>
#include <type_traits>

template <typename T>
class A {
    private:
    template <typename U>
    static typename std::enable_if<std::is_same<U, char>::value, char>::type
    g() {
        std::cout << "char\n";
        return char();
    }

    template <typename U>
    static typename std::enable_if<std::is_same<U, int>::value, int>::type
    g() {
        std::cout << "int\n";
        return int();
    }

    public:
    static T f() { return g<T>(); }
};

int main(void)
{
    A<char>::f();
    A<int>::f();
    // error: no matching function for call to ‘A<double>::g()’
    // A<double>::f();
    return 0;
}
3
votes

Tag dispatching is often the clean way to do this.

In your base method, use a traits class to determine what sub version of the method you want to call. This generates a type (called a tag) that describes the result of the decision.

Then perfect forward to that implememtation sub version passing an instance of the tag type. Overload resolution kicks in, and only the implememtation you want gets instantiated and called.

Overload resolution based on a parameter type is a much less insane way of handling the dispatch, as enable_if is fragile, complex at point of use, gets really complex if you have 3+ overloads, and there are strange corner cases that can surprise you with wonderful compilation errors.

1
votes

Maybe i'm wrong but chosen best anwser provided by Manu343726 has an error and won't compile. Both operator overloads have the same signature. Consider best anwser in question std::enable_if : parameter vs template parameter

P.S. i would put a comment, but not enough reputation, sorry