A possible implementation would be using the double dispatching:
#include <iostream>
#include <list>
struct visitor;
struct dispatchable {
virtual void accept(visitor &v) = 0;
};
template <class>
struct base;
struct visitor {
template<typename T>
void visit(base<T> &);
};
template <class T>
struct base: dispatchable {
T val;
base(T newVal): val(newVal) {};
void accept(visitor &v) override { v.visit(*this); }
};
struct derivedInt : base<int> {
derivedInt(int newVal): base(newVal) {};
};
struct derivedDouble : base<double> {
derivedDouble(double newVal): base(newVal) {};
};
template<>
void visitor::visit(base<int> &) {
std::cout << "int" << std::endl;
}
template<>
void visitor::visit(base<double> &) {
std::cout << "double" << std::endl;
}
int main ( void ) {
visitor v{};
std::list <dispatchable*> coll;
coll.push_back(new derivedInt{42});
coll.push_back(new derivedDouble{.42});
for(auto d: coll) d->accept(v);
}
This way, you have only to define the specialized function that deals with the new base<T>
type you want to introduce.
As an example, if you want to use base<char>
, you have to define:
template<>
void visitor::visit(base<char> &) {
std::cout << "char" << std::endl;
}
Note that I supposed you want to treat each specialization of base<T>
in a different way. Otherwise, it's enough to define the generic member function visitor::visit
and drop the specializations.
Side note: do not use naked pointers.
This is an example. In production code, I'd use smart pointers instead.