如果您的程序知道将要测试的所有子类型,则可以使用返回指向子类型的指针的虚拟接口。正如downvotes 和评论所指出的,这不是最灵活的方法,因为它要求基类了解所有派生类。但是,它非常快。因此,需要在灵活性与性能之间进行权衡。
class Base {
//...
virtual A * is_A () { return 0; }
virtual B * is_B () { return 0; }
//...
template <typename MAYBE_DERIVED>
MAYBE_DERIVED * isTypeOrSubtype () {
//...dispatch to template specialization to call is_X()
}
};
//...
class A : virtual public Base {
//...
A * is_A () { return this; }
};
在 IDEONE 上,建议的技术比使用动态转换快 20 到 50 倍。1实现使用宏允许将新类添加到单个位置,然后以自动方式对基类进行适当的扩展。
(1) - 我最初将它的时钟速度提高了近 100 倍,但这没有isTypeOrSubtype()
我添加的用于模拟所需接口的包装器方法。
如果灵活性比性能具有更高的价值,那么性能稍差的解决方案是使用 amap
来关联类型和相应的指针值(拥有指针值消除了对动态转换的需要)。实例在map
基类中维护,关联由子类的构造函数进行。使用常规map
还是 aunordered_map
取决于有多少子类实际上继承了基类。我想这些数字会很小,所以一个普通的map
就足够了。
class Base {
std::map<const char *, void *> children_;
//...
template <typename MAYBE_DERIVED>
MAYBE_DERIVED * isTypeOrSubtype () {
auto x = children_.find(typeid(MAYBE_DERIVED).name());
return ((x != children_.end())
? static_cast<MAYBE_DERIVED *>(x->second)
: 0);
}
};
//...
class A : virtual public Base {
//...
A () { children_[typeid(A).name()] = this; }
//...
};
在 IDEONE 上,第二个建议比使用 dynamic cast快 10 到 30 倍。我不认为 IDEONE 编译时会进行优化,所以我希望时间更接近于生产构建的第一个建议。实现的机制用作typeid(...).name()
映射的键。2
(2) - 这假定typeid(...).name()
返回类似于字符串文字的内容,并且在对相同类型进行操作时始终返回相同的字符串指针。如果您的系统不是这样运行的,您可以修改map
以将 astd::string
作为键,但性能会降低。