2

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?

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
S.V
  • 2,149
  • 2
  • 18
  • 41
  • Why do you want to call `c3.f` like this, with the ``? What do you want to achieve? This looks a bit strange to me, maybe this is an XY problem, and there is some more straight forward solution to the underlying use case. – florestan Dec 03 '21 at 20:59
  • In my real problem, the function `f` sends some information to a database. The function might be given as input different object types. What exactly is sent is computed by using an object of type `A` (which is given to `C3` constructor) and so it depends on the class `C3` template parameter. Class `C3` is a template class which inherits from another template class and it is an input parameter to other templated algorithm, and so I can not change the interface. – S.V Dec 03 '21 at 21:08
  • Ok, and the specialised template parameter, in your example, does reflect the return type of the function `g`? In that case there would be an easy solution, because the system is actually overparametrized. – florestan Dec 03 '21 at 21:14
  • The real code calls several member functions of class `A` to transform the argument of function `f` of type `B` into information which will be sent. The database table and its columns depend on `B` and how the numbers are computed depends on `A`. Think of type `A` as a calculator type, while `B` is the input type. – S.V Dec 03 '21 at 21:24

1 Answers1

2

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.

S.V
  • 2,149
  • 2
  • 18
  • 41
florestan
  • 4,405
  • 2
  • 14
  • 28