Reputation: 397
I am trying to implement a family of templated objects that will be accessed through a non-template virtual base class pointer. Simplified, the base class looks like:
class Base
{
public:
virtual void printThing(const int &thing) = 0;
virtual void printThing(const double &thing) = 0;
virtual void printThing(const bool &thing) = 0;
};
What I'd like to do is sketched out in the derived class implementation below:
#include <iostream>
template <typename T>
class Derived : public Base
{
public:
void printThing(const T &thing);
template <typename U>
void printThing(const U &thing);
};
template <typename T>
void Derived<T>::printThing(const T &thing)
{
std::cout << "Derived same type " << thing << std::endl;
}
template <typename T>
template <typename U>
void Derived<T>::printThing(const U &thing)
{
std::cout << "Derived diff type " << thing << std::endl;
}
template <>
template <>
void Derived<double>::printThing(const int &thing)
{
std::cout << "Derived<double> specialized for int " << thing << std::endl;
}
And this works on U of any type - as long as the code calls the member functions directly on an instance of Derived, and U is known at compile time.
But I get compiler errors when I try to access Derived through a pointer to Base, as indicated by the test program below:
int main(int argc, char* argv[])
{
Derived<int> dint;
Derived<double> ddouble;
Base * bint = &dint;
Base * bdouble = &ddouble;
double d = 3.14;
int i = 42;
bint->printThing(i);
bint->printThing(d);
bdouble->printThing(i);
bdouble->printThing(d);
return 0;
}
clang++ on Mac OS 10.8.5 gives this feedback:
razor:playpen cfry$ clang++ template-specialization.cc
template-specialization.cc:43:16: error: variable type 'Derived<int>' is an abstract class
Derived<int> dint;
^
template-specialization.cc:7:16: note: unimplemented pure virtual method 'printThing' in 'Derived'
virtual void printThing(const double &thing) = 0;
^
template-specialization.cc:8:16: note: unimplemented pure virtual method 'printThing' in 'Derived'
virtual void printThing(const bool &thing) = 0;
^
template-specialization.cc:44:19: error: variable type 'Derived<double>' is an abstract class
Derived<double> ddouble;
^
template-specialization.cc:6:16: note: unimplemented pure virtual method 'printThing' in 'Derived'
virtual void printThing(const int &thing) = 0;
^
template-specialization.cc:8:16: note: unimplemented pure virtual method 'printThing' in 'Derived'
virtual void printThing(const bool &thing) = 0;
^
2 errors generated.
razor:playpen cfry$
Note that I have explicitly implemented a Derived<double>::printThing(const int &)
, which the compiler claims does not exist. And the generalized Derived<T>::printThing(const U &)
member functions don't appear to get instantiated.
Is there any portable way to tell the compiler that I intend the generalized template member function to be instantiated for each of the "unimplemented" virtual methods?
I have tried a bunch of alternatives, but so far the only one that works is to give the base class member functions a default implementation, and write wrappers of Derived which explicitly implement printThing() for the required U types.
Upvotes: 0
Views: 1439
Reputation: 397
I've found an answer using the Curiously Recurring Template Pattern (CRTP), explained here by Eli Bendersky.
It requires adding another layer of template class:
template <class Child>
class Adapter : public Base
{
public:
void printThing(const int &thing)
{
static_cast<Child *>(this)->printThingInternal(thing);
}
void printThing(const double &thing)
{
static_cast<Child *>(this)->printThingInternal(thing);
}
void printThing(const bool &thing)
{
static_cast<Child *>(this)->printThingInternal(thing);
}
};
template <typename T>
class Derived : public Adapter<Derived <T> >
{
public:
void printThingInternal(const T &thing);
template <typename U>
void printThingInternal(const U &thing);
};
With this addition, and the simple change to Derived to inherit from Adapter, the program placates the compiler, and better yet, generates the results I was looking for:
razor:playpen cfry$ clang++ template-specialization.cc
razor:playpen cfry$ ./a.out
Derived same type 42
Derived diff type 3.14
Derived<double> specialized for int 42
Derived same type 3.14
razor:playpen cfry$
Upvotes: 2