Reputation: 11037
I can use say typeid
or dynamic_cast
to switch between derived base class instances. For example:
struct B { virtual ~B() {} };
struct D1 : B {};
struct D2 : B {};
void doit(B &b) {
if (dynamic_cast<D1 *>(&b)) foo1();
else if (dynamic_cast<D2 *>(&b)) foo2();
}
How can I continue in this vein when the derived type is a template instantiation? For example, how could I extend the doit
function above to handle four cases; perhaps using the DT1
and DT2
classes below, instantiated with arbitrary types?
template <typename T>
struct DT1 : B {};
template <typename T>
struct DT2 : B {};
Upvotes: 2
Views: 65
Reputation: 15334
Instead of hardcoding the list of derived classes in the doit
function you could maintain some sort of registry of foo
functions to call for each type. In the simplest form this could just be a vector
of std::function<void(B&)>
and you just loop through and call each of them. Each std::function
would be responsible for checking if the type matches:
auto& doitRegistry(){
static std::vector<std::function<void(B&)>> registry;
return registry;
}
void doit(B &b) {
for (auto& f : doitRegistry()) {
f(b);
}
}
If you wanted to be smarter the registry could be something like a std::unordered_map<std::type_index, std::function<void(B&)>>
so that you don't have to loop through the whole registry but as long as you don't have a huge number of derived classes it probably doesn't matter much.
Then you just have to register each type of derived class in the registry. You can create a helper class that registers a function in the registry in its constructor:
struct DoItRegistration {
DoItRegistration(std::function<void(B&)> foo) {
doitRegistry().push_back(std::move(foo));
}
};
And create a static instance of this class per derived class:
template <typename T>
struct DT1 : B {
DT1() {
registerWithDoIt();
}
static DoItRegistration& registerWithDoIt() {
static DoItRegistration registration([](B &b){
if (dynamic_cast<DT1<T>*>(&b)){
foo1();
}
});
return registration;
}
};
So that on first use of a derived class it registers itself with the doit
registry and will be called when needed:
int main() {
DT1<double> d1;
DT1<int> d2;
DT2<double> d3;
DT1<double> dupeType;
doit(d3); // calls foo2()
}
Upvotes: 1