获取派生模板实例化的运行时类型

Obtaining the runtime type of a derived template instantiation

本文关键字:运行时 类型 实例化 派生 获取      更新时间:2023-10-16

我可以使用typeiddynamic_cast在派生的基类实例之间切换。例如:

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();
}

当派生类型是模板实例化时,我如何继续这样做?例如,我如何扩展上面的doit函数来处理四种情况;也许使用下面的DT1DT2类,用任意类型实例化?

template <typename T>
struct DT1 : B {};
template <typename T>
struct DT2 : B {};

与其在doit函数中硬编码派生类的列表,不如维护某种foo函数的注册表来调用每种类型。在最简单的形式中,这可能只是std::function<void(B&)>的一个vector,您只需循环并调用它们中的每一个。每个std::function将负责检查类型是否匹配:

auto& doitRegistry(){
static std::vector<std::function<void(B&)>> registry;
return registry;
}
void doit(B &b) {
for (auto& f : doitRegistry()) {
f(b);
}
}  

如果你想更聪明,注册表可以像std::unordered_map<std::type_index, std::function<void(B&)>>这样你就不必遍历整个注册表,但只要你没有大量的派生类,它可能就无关紧要。

然后,您只需在注册表中注册每种类型的派生类。可以创建一个帮助程序类,该类在其构造函数的注册表中注册函数:

struct DoItRegistration {
DoItRegistration(std::function<void(B&)> foo) {
doitRegistry().push_back(std::move(foo));
}
};

并为每个派生类创建此类的静态实例:

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;
}
};

因此,在首次使用派生类时,它会向doit注册表注册自身,并在需要时调用:

int main() {
DT1<double> d1;
DT1<int> d2;
DT2<double> d3;
DT1<double> dupeType;
doit(d3);  // calls foo2()
}

现场演示。