35
votes

What is the C++ syntax for specializing a template function that's inside a template class? For example, consider that I have the following two classes and their usage. I would like to be able to provide specialized implementations of the method X::getAThing() for different types. E.g.: int, std::string, arbitrary pointer or class, etc.

template <class c1> class X {
public:
   template<typename returnT> returnT getAThing(std::string param);
   static std::string getName();
private:
   c1 theData;
};

// This works ok...
template <class c1> std::string X<c1>::getName() {
   return c1::getName();
}

// This blows up with the error:
// error: prototype for 'int X<c1>::getAThing(std::string)' does not match any in class 'X<c1>'
template <class c1> template <typename returnT> int X<c1>::getAThing(std::string param) {
   return getIntThing(param); // Some function that crunches on param and returns an int.
}

// More specialized definitions of getAThing() for other types/classes go here...

class Y {
public:
   static std::string getName() { return "Y"; }
};

int main(int argc, char* argv[])
{
   X<Y> tester;
   int anIntThing = tester.getAThing<int>(std::string("param"));
   cout << "Name: " <<  tester.getName() << endl;
   cout << "An int thing: " << anIntThing << endl;
}

I've been trying to guess at the correct syntax for the specialization for at least an hour, and can't figure anything out that will compile. Any help would be greatly appreciated!

6

6 Answers

20
votes

AFAIK (and the standards experts can correct me), you cannot specialize a templated function of a class template without specializing the class itself...

i.e. the following I think will work:

template <> template <> int X<Y>::getAThing<int>(std::string param) {
   return getIntThing(param); // Some function that crunches on param and returns an int.
}
8
votes

C++ has no concept of partial specialization for function templates. However, you can get the same effect as full specialization through function overloading.

I am assuming you have something like this, which is really one of the only ways to do it.

template<class TYPE>
class MyInterface {
public:
    template<class RETURN>
    RETURN myFunction(RETURN& ref, ....);
};

In this case, you specialize "myFunction()" by declaring ordinary member functions with the desired type. C++'s function overloading rules should give you what you want, e.g.

template<class TYPE>
class MyInterface {
public:
    template<class RETURN>
    RETURN myFunction(RETURN& ref, ....);

    // String specialization
    std::string myFunction(std::string& ref, ...);
};

The compiler will use the "std::string" function where appropriate, and may never use the inner template at all.

7
votes

So, I'm taking a different approach to answering your question. I'm going to start from something that sort of does what you want, and works. And then maybe we can figure out how to permute it into something closer to what you really want:

#include <string>
#include <iostream>

int getIntThing(const ::std::string &param);

template <typename returnT>
returnT getThingFree(const ::std::string &param);

template <>
int getThingFree<int>(const ::std::string &param)
{
   return getIntThing(param);
}

// More specialized definitions of getAThing() for other types/classes
// go here...

template <class c1> class X {
public:
   template<typename returnT> returnT getAThing(std::string param);
   static std::string getName();
private:
   c1 theData;
};

// This works ok...
template <class c1> std::string X<c1>::getName() {
   return c1::getName();
}

// This also works, but it would be nice if I could explicitly specialize
// this instead of having to explicitly specialize getThingFree.
template <class c1>
template <class RT>
RT X<c1>::getAThing(std::string param) {
   // Some function that crunches on param and returns an RT.
   // Gosh, wouldn't it be nice if I didn't have to redirect through
   // this free function?
   return getThingFree<RT>(param);
}

class Y {
public:
   static std::string getName() { return "Y"; }
};

int main(int argc, char* argv[])
{
   using ::std::cout;
   X<Y> tester;
   int anIntThing = tester.getAThing<int>(std::string("param"));
   cout << "Name: " <<  tester.getName() << '\n';
   cout << "An int thing: " << anIntThing << '\n';
}

Here is another idea that sort of works, and isn't exactly what you want, but is closer. I think you've thought of it yourself. It's also rather ugly in the way it uses type deduction.

#include <string>
#include <iostream>

template <class c1> class X;

int getIntThing(const ::std::string &param)
{
   return param.size();
}

// You can partially specialize this, but only for the class, or the
// class and return type. You cannot partially specialize this for
// just the return type. OTOH, specializations will be able to access
// private or protected members of X<c1> as this class is declared a
// friend.
template <class c1>
class friendlyGetThing {
 public:
   template <typename return_t>
   static return_t getThing(X<c1> &xthis, const ::std::string &param,
                            return_t *);
};

// This can be partially specialized on either class, return type, or
// both, but it cannot be declared a friend, so will have no access to
// private or protected members of X<c1>.
template <class c1, typename return_t>
class getThingFunctor {
 public:
   typedef return_t r_t;

   return_t operator()(X<c1> &xthis, const ::std::string &param) {
      return_t *fred = 0;
      return friendlyGetThing<c1>::getThing(xthis, param, fred);
   }
};

template <class c1> class X {
public:
   friend class friendlyGetThing<c1>;

   template<typename returnT> returnT getAThing(std::string param) {
      return getThingFunctor<c1, returnT>()(*this, param);
   }
   static std::string getName();
private:
   c1 theData;
};

// This works ok...
template <class c1> std::string X<c1>::getName() {
   return c1::getName();
}

class Y {
public:
   static std::string getName() { return "Y"; }
};

template <class c1>
class getThingFunctor<c1, int> {
 public:
   int operator()(X<c1> &xthis, const ::std::string &param) {
      return getIntThing(param);
   }
};

// More specialized definitions of getAThingFunctor for other types/classes
// go here...

int main(int argc, char* argv[])
{
   using ::std::cout;
   X<Y> tester;
   int anIntThing = tester.getAThing<int>(std::string("param"));
   cout << "Name: " <<  tester.getName() << '\n';
   cout << "An int thing: " << anIntThing << '\n';
}

I would recommend declaring getThingFunctor and friendlyGetThing in a semi-private utility namespace.

1
votes

For the curious, this is probably the solution I'm going to go with in my own code. This is a slight variation on Omnifarious's answer, that eliminates the need for an extra class. I still give props to Omnifarious, as he did most of the leg work:

#include <iostream>
#include <string>

using namespace std;

// IMPORTANT NOTE: AdaptingFunctor has no access to the guts of class X!
// Thus, this solution is somewhat limited.
template<typename t1> class AdaptingFunctor {
public:
   t1 operator() (string param);
};

// Can specialize AdaptingFunctor for each type required:
template<> int AdaptingFunctor<int>::operator() (string param)
{
   return param.size(); // <=== Insert required type-specific logic here
}

// Additional specializations for each return type can go
// here, without requiring specialization of class c1 for X...


template <class c1> class X {
public:
   template<typename returnT>  returnT getAThing(std::string param)
      {
     AdaptingFunctor<returnT> adapter;
     return adapter(param);
      }
   static std::string getName();
private:
   c1 theData;
};

// Template definition of class method works ok...
template <class c1> std::string X<c1>::getName() {
   return c1::getName();
}

class Y {
public:
   static std::string getName() { return "Y"; }
};


int main(int argc, char* argv[])
{
   X<Y> tester;
   int anIntThing = tester.getAThing<int>(std::string("param"));
   cout << "Name: " <<  tester.getName() << endl;
   cout << "An int thing: " << anIntThing << endl;
}
-2
votes

Try

template <>
template <class T>
int X<T>::template getAThing<int>(std::string param)
{
   return getIntThing(param);
}

This still doesn't compile, but it's closer than you had.

I think you can't specialize members en masse. You have to specify a particular specialization of the class template before you can start specializing its members.

-2
votes

Here's the simplest, easiest way I've seen so far to do this:

template <class T1>
struct MyClass {
  template <class T2>
  void MyFunction();
};

template <class T1>
template <class T2>
void MyClass<T1>::MyFunction() {  // NOTE:  NO T2 on this line.
  // Code goes here
}