How to call a member function of the primary class template from the member function of a specialization

654 views Asked by At

Can I call an unspecialized template method from a specialized one?

This is easy when using inheritance:

class SomeBaseClass {
  virtual void DoWork() { /* Do something */ }
};

class SomeClass : public SomeBaseClass {
  void DoWork() {
    // Do something first
    SomeBaseClass::DoWork();
  }
};

But is a bit different when using templates:

template <class T>
class SomeClass {
  void DoWork();
};

template<class T>
void SomeClass<T>::DoWork() { /* Do something */}

template<>
void SomeClass<int>::DoWork() {
   // Do something first
   DoWork<>(); // Call method from line 8
}

My generic DoWork function has a lot of really good code in it that I'd hate to duplicate. My specialized one just has an extra step that it needs to perform when a specific type is used.

2

There are 2 answers

1
AndyG On BEST ANSWER

You're thinking about this the wrong way. The solution is not to have your class specialized, but to have your function specialized. What I mean here is to use tag dispatch

That is, declare two private helper functions in your class named DoWorkHelper, one of which is overloaded for the specialized type, and the other not.

The way we do this is to wrap our type in a 'tag' that is basically an empty struct, and then specialize the tag for our type of interest:

namespace SomeClassDetail{
template<class T>
struct specialized_tag : std::false_type{};

template<>
struct specialized_tag<int>: std::true_type{};
}

true_type and false_type are essentially wrappers for boolean true and false. They're nice because they're types and not values (and when we template we care all about types)

Next, we'll declare our class with aforementioned overloads:

template <class T>
class SomeClass {
public:
  void DoWork();
  
  private:
  void DoWorkHelper(std::true_type);
  void DoWorkHelper(std::false_type);
};

The idea here is that true_type means "Yes, this function is for the specialized version!"

Here's what the definitions look like:

template<class T>
void SomeClass<T>::DoWork()
{ 
    DoWorkHelper(typename SomeClassDetail::specialized_tag<T>::type{});
}

template<class T>
void SomeClass<T>::DoWorkHelper(std::true_type)
{
   std::cout << "Specialized DoWork\n";
   DoWorkHelper(std::false_type());
}
  
template<class T>
void SomeClass<T>::DoWorkHelper(std::false_type)
{
    std::cout << "Unspecialized DoWork\n";
}

That's it. The specialized version will do its thing, and then call the unspecialized version, and the unspecialized version (for all other T), will simply do its thing.

Here's a live demo that demonstrates tag dispatch in action

2
EmDroid On

Similar to here, you can do that indirectly:

template <class T>
class SomeClassCommonImpl {
  void DoWork();
};

template<class T>
void SomeClassCommonImpl<T>::DoWork() { /* Do something */}

template <class T>
class SomeClass: public SomeClassCommonImpl<T> {
  // use the default implementation
};

template <>
class SomeClass<int>: public SomeClassCommonImpl<int> {
  void DoWork();
};

template<>
void SomeClass<int>::DoWork() {
   // Do something first
   SomeClassCommonImpl<int>::DoWork<>(); // Call the common method
}