我在某个项目中创建了以下代码:
std::vector<Base*> objs_;
template <class TDerived>
T* Get() {
auto objIt = std::find_if(objs_.cbegin(), objs_.cend(), [](Base* it) {
return typeid(TDerived).name() == typeid(*it).name();
});
return objIt == objs_.cend() ? nullptr : *objIt;
}
我想弄清楚,使用
typeid
是一个不错的选择吗?
因为C++有dynamic_cast
例如:
template <class TDerived>
T* Get() {
auto objIt = std::find_if(objs_.cbegin(), objs_.cend(), [](Base* it) {
return dynamic_cast<T*>(it) != nullptr;
});
return objIt == objs_.cend() ? nullptr : *objIt;
}
(我知道上面的解决方案之间存在语义差异,因为
dynamic_cast
也会抱怨TDerived
继承者,但是没关系,所有使用的TDerived
都是final
)
typeid
是一个好的选择,还是 dynamic_cast
更好或者有更好的解决方案?
你不能比较名称,因为它们不必为相同的类型具有相同的指针值。例如,
typeid(TDerived).name() == typeid(TDerived).name()
可以是假的。
std::type_info::operator==
:
return typeid(TDerived) == typeid(*it);
至于这是否比
dynamic_cast
更好,那要看情况了。对于您提到的最后一堂课,语义上没有区别。我认为比较typeid
s会让你的意图更清晰。
typeid
根本不是一个好主意,因为子类型之一涉及的类型将需要丰富检查typeid
的部分。这又是开/闭原则。
顺便说一句,
typeid
有很多微妙的问题,例如返回的类型名称没有标准化,而且,正如在 cppreference 上指出的那样:
不能保证同一个 std::type_info 实例将被同一类型上 typeid 表达式的所有评估所引用,尽管它们比较相等,这些 type_info 对象的 std::type_info::hash_code 将是相同的,就像他们的 std::type_index 一样。
dynamic_cast
的使用稍微好一些,因为如果不添加新类型,它可能允许遵守开放/封闭原则。但如果这样做,您将再次丰富代码。此外,dynamic_cast
要求使用类/函数了解很多关于使用的类。这可能会削弱 encapsulation 并创建隐藏的耦合(即您不能再根据需要更改使用的类,因为您轻而易举地打破了一些假设)
最好的办法是用多态的方式重写代码。 C++ 核心指南在这方面提醒说,virtual functions should be preferred to casting。更一般地说,该方法应该使用告诉不问原则,让多态代码做它必须做的事情。或者选择访客模式。