Search code examples
c++templatestemplate-specializationfunction-templatesclass-template

Template member function specialization of a templated class without specifying the class template parameter


What is the correct syntax to specialize a templated member function of a templated class without specifying the class template parameter?

Here is what I mean:

Example #1 (works):

#include <iostream>

struct C1
{
 template <class B>
 void f(void) const;
};

template <>
void C1::f<int>(void) const { std::cout<<777<<std::endl; }

int main(void)
{
 C1 c1; c1.f<int>();
}

Example #2 (works):

#include <iostream>

template <class A>
struct C2
{
 template <class B>
 void f(void) const;
};

template <>
template <>
void C2<int>::f<int>(void) const { std::cout<<888<<std::endl; }

int main(void)
{
 C2<int> c2; c2.f<int>();
 return 0;
}

Example #3 (does not compile: "enclosing class templates are not explicitly specialized"):

#include <iostream>

template <class A>
struct C3
{
 template <class B>
 void f(void) const;
};

struct D { static int g(void){ return 999; } };

template <class A>
template <>
void C3<A>::f<int>(void) const { std::cout<<A::g()+1<<std::endl; }

template <class A>
template <>
void C3<A>::f<char>(void) const { std::cout<<A::g()+2<<std::endl; }

int main(void)
{
 C3<D> c3a; c3a.f<int >(); // expect to see 1000
 C3<D> c3b; c3b.f<char>(); // expect to see 1001
 return 0;
}

How can I make example #3 work?


Solution

  • You can use a technique called tag dispatch and replace the template specialisations by function overloads.

    template<typename>
    struct Tag {};
    
    template <class A>
    struct C3
    {
     void f_impl(Tag<int>) const;
     void f_impl(Tag<char>) const;
     template<class B>
     void f() const {
         f_impl(Tag<B>{});
     }
    };
    
    struct D { static int g(void){ return 999; } };
    
    template <class A>
    void C3<A>::f_impl(Tag<int>) const { std::cout<<A::g()+1<<std::endl; }
    
    template <class A>
    void C3<A>::f_impl(Tag<char>) const { std::cout<<A::g()+2<<std::endl; }
    

    Then your call site looks exactly as you want:

     C3<D> c3; c3.f<int>();  // expect to see 1000
     C3<D> c4; c4.f<char>(); // expect to see 1001
    

    Full example here.