Search code examples
c++c++11templatesvariadic-templatespartial-specialization

Function template overloading - partial specialization


I have 2 classes that follow a similar pattern:

Foo.h

// header guard here 

class Foo {
public:
    Foo() = delete;

    static foo1& getFoo1( Param a, Param b, Param c ) {
        // code...
    }

    static foo2& getFoo2( Param a, Param b, Param c ) {
        // code...
    }

    // generic function to return appropriate Foo Type based on template argument 
    template<class FooType>
    static FooType& getFoo( Param a, Param b, Param c );
};

#endif

Foo.cpp

#include <Foo.h>

// specializations of getFoo() for each type
template<>
foo1& Foo::getFoo( Param a, Param b, Param c ) {
    return getFoo1( a, b, c );
}

template<>
foo2& Foo::getFoo( Param a, Param b, Param c ) {
    return getFoo2( a, b, c );
}

Foo above compiles just fine. Bar on the other hand has a similar structure or pattern as Foo above; the only difference is that it's static getBar1(), getBar2() etc. are not just normal functions; they are function templates.

Bar.h

// header guard

class Bar {
public:
    Bar() = delete;

    template<class IntType = int>
    static bar1<IntType>& getBar1( IntType a, IntType b ) {
        // code...
    }

    template<class RealType = double>
    static bar2<RealType>& getBar2( RealType a, RealType b ) {
        // code...
    }

    template<class IntType = int>
    static bar3<IntType>& getBar3( IntType a ) {
        // code...
    }

    template<class RealType = double>
    static bar4<RealType>& getBar4( RealType a ) {
        // code...
    }

    // ...

    template<class RealType = double>
    static bar12<RealType>& getBar12() {
        // code...
    }

    template<class RealType = double, class A, class B>
    static bar12&<RealType>& getBar12( A a1, A a2, B b1 ) {
        // code...
    }

    template<class RealType = double, class X>
    static bar12&<RealType>& getBar12( std::initialize_list<double> list, X x ) {
        // code...
    }

    template<class RealType = double, class X>
    static bar12&<RealType>& getBar12( std::size_t size, RealType a, RealType b, X x ) {
        // code..
    }

    // Here is where I start to get into problems:
    // I'm trying to do something similar to what I've done above in Foo for a generic function template.
    template<typename Type, template<typename> class BarType, class... FuncParams>
    static BarType<Type>& getBar( FuncParams... params );

}; 

#endif

Bar.cpp

#include "Bar.h"

// specializations of getBar() for each type
template<typename Type, class... FuncParams>
bar1<Type>& Bar::getBar( FuncParams... params ) {
    return getBar1( params... );
}

template<typename Type, class... FuncParms>
bar2<Type>& Bar::getBar( FuncParams... params ) {
    return getBar2( params... );
}

Why is it that when I begin to added a class type that is a class template; everything seems to break. The first class above compiles and returns back the appropriate Foo. However, in the second class Bar I keep getting compiler errors that function definition is not matching an existing declaration.

This question is related to this one here: Specializing and or Overloading member function templates with variadic parameters

This question is specifically about why the one does compile and the other one doesn't.


Solution

  • The first class above compiles and returns back the appropriate Foo

    Yes, because

    template<>
    foo1& Foo::getFoo( Param a, Param b, Param c ) {
        return getFoo1( a, b, c );
    }
    

    it's a full specialization of the template method getFoo()

    template<class FooType>
    static FooType& getFoo( Param a, Param b, Param c );
    

    with the FooType type fixed to foo1.

    And you can make a full specialization of a template function (or method).

    However, in the second class Bar I keep getting compiler errors that function definition is not matching an existing declaration.

    Sure.

    Because you're trying to partial specialize the template method getBar()

    template<typename Type, template<typename> class BarType, class... FuncParams>
    static BarType<Type>& getBar( FuncParams... params );
    

    fixing BarType to bar1

    template<typename Type, class... FuncParams>
    bar1<Type>& Bar::getBar( FuncParams... params ) {
        return {};//getBar1( params... );
    }
    

    But you can't partial specialize a template function/method. It's forbidden by the language.

    If you want something similar, you have to pass through the partial specialization of a struct (or class).

    --- EDIT ---

    The OP ask

    You said, "you have to pass through the partial specialization of a struct( or class)." Okay; so there is a work around: would you be able to provide a small basic example?

    There are many ways to use partial specialization of structs (classes) to bypass the non-partial-specialization limits for functions/methods.

    In the following basic example I propose a template foo struct with a template func() method. The single template parameter for foo is the type returned by func(); the variadic template type list for func() is the list of types of arguments.

    But you can play this game in differents modes.

    #include <iostream>
    
    template <typename>
    struct bar1
     { template <typename ... Args> bar1 (Args && ...) { } };
    
    template <typename>
    struct bar2
     { template <typename ... Args> bar2 (Args && ...) { } };
    
    template <typename>
    struct foo;
    
    template <typename T>
    struct foo<bar1<T>>
     {
       template <typename ... Args>
       static bar1<T> func (Args && ... as)
        { return { std::forward<Args>(as)... }; }
     };
    
    template <typename T>
    struct foo<bar2<T>>
     {
       template <typename ... Args>
       static bar2<T> func (Args && ... as)
        { return { std::forward<Args>(as)... }; }
     };
    
    int main()
     {
       foo<bar1<int>>::func(1, "two", 3.0);
       foo<bar2<long>>::func(4.0f, "five", 6L);
     }