我有一棵树,每个节点基本上都是这样的:
struct node
{
std::unordered_set<object*> objects;
std::map<std::type_index,node> children;
};
当我遍历树以添加新类型时,我想进行检查:
std::is_base_of<base,derived>
但是,我对派生类型的唯一信息是type_index/type_info*
。
无论如何我可以将type_info*
转换为template
参数吗?
如果没有,我的其他选择是什么?我想可以调用decltype(*objects.begin())
,但这需要每个节点中的每个集都永远不会为空。
答案 0 :(得分:6)
无论如何我可以将
type_info*
转换为模板参数吗?
不,没有办法。模板是编译时的事情,RTTI是运行时。两者之间没有联系。
我想可以调用
decltype(*objects.begin())
,但这需要每个节点中的每个集都永远不会为空。
不需要。 decltype
不评估其论点 - 它不需要。它只需要检查类型。您可以愉快地执行decltype(*a_null_pointer)
而不调用UB,因为表达式永远不会被评估 - 这是一个所谓的未评估的上下文。 sizeof
属于同一类别。
请注意,这并不会给你带来太大的影响 - 你只会得到object*&
。如果没有先准备映射,通常无法从运行时信息中获取类型。
答案 1 :(得分:4)
is_base_of
和decltype
纯粹是编译时构造;它们不会反映对象的动态类型。
type_info
不提供检查子类关系的工具;在运行时执行该检查的唯一方法是使用dynamic_cast
。您需要存储unique_ptr<base>
并查看它是否可以dynamic_cast
到派生类型。
答案 2 :(得分:4)
如果我理解正确,你需要一个类型inheritance_checker
,使得它的每个实例与一个类型相关联,但inheritance_checker
本身不是。类似于type_ifo
但可以在运行时检查继承关系的东西。例如,您希望以下工作:
class A {};
class B : public A {};
// Note that a and b have the same type but they are "related" to distinct types.
inheritance_checker a = inheritance_checker::create<A>();
inheritance_checker b = inheritance_checker::create<B>();
assert( a.is_base_of (b) );
assert( a.derives_from(a) ); // derives from or is equal to
assert( ! b.is_base_of (a) );
assert( b.derives_from(b) ); // derives from or is equal to
如果是这种情况,我可以建议你,不幸的是,这可能会很慢!这取决于例外情况。
基本的想法是,如果你掷出B*
,那么catch (A*)
就是匹配。因此,我们赋予inheritance_checker
投掷和捕获指向施工时给定类型的指针的能力。但inheritance_checker
不是模板,因此必须以类型擦除的方式提供此容量。这可以通过将指针存储到具有固定签名的函数来完成,该签名不依赖于在构造中传递的类型,但能够抛出/捕获指向给定类型的指针。 inheritance_checker
的可能实现如下。
#include <cassert>
class inheritance_checker {
typedef void (*thrower_t)();
typedef bool (*catcher_t)(thrower_t);
public:
template <typename T>
static inheritance_checker create() {
return inheritance_checker(concrete_thrower<T>, concrete_catcher<T>);
}
bool is_derived_from(const inheritance_checker& other) const {
return other.catcher_(thrower_);
}
bool is_base_of(const inheritance_checker& other) const {
return catcher_(other.thrower_);
}
private:
template <typename T>
static void concrete_thrower() {
throw static_cast<T*>(nullptr);
}
template <typename T>
static bool concrete_catcher(thrower_t thrower) {
try { thrower(); }
catch (T*) { return true; }
catch (...) { }
return false;
}
inheritance_checker(thrower_t thrower, catcher_t catcher) :
thrower_(thrower), catcher_(catcher) {
}
thrower_t thrower_;
catcher_t catcher_;
};
class A {};
class B : public A {};
class C : public B {};
class D {};
int main() {
auto a = inheritance_checker::create<A>();
auto b = inheritance_checker::create<B>();
auto c = inheritance_checker::create<C>();
auto d = inheritance_checker::create<D>();
assert( a.is_base_of(a));
assert( a.is_base_of(b));
assert( a.is_base_of(c));
assert(!a.is_base_of(d));
assert( a.is_derived_from(a));
assert(!a.is_derived_from(b));
assert(!a.is_derived_from(c));
assert(!a.is_derived_from(d));
assert(!b.is_base_of(a));
assert( b.is_base_of(b));
assert( b.is_base_of(c));
assert(!b.is_base_of(d));
assert( b.is_derived_from(a));
assert( b.is_derived_from(b));
assert(!b.is_derived_from(c));
assert(!b.is_derived_from(d));
assert(!c.is_base_of(a));
assert(!c.is_base_of(b));
assert( c.is_base_of(c));
assert(!c.is_base_of(d));
assert( c.is_derived_from(a));
assert( c.is_derived_from(b));
assert( c.is_derived_from(c));
assert(!c.is_derived_from(d));
assert(!d.is_base_of(a));
assert(!d.is_base_of(b));
assert(!d.is_base_of(c));
assert( d.is_base_of(d));
assert(!d.is_derived_from(a));
assert(!d.is_derived_from(b));
assert(!d.is_derived_from(c));
assert( d.is_derived_from(d));
}
如果您愿意,可以将type_info*
成员添加到inheritance_checker
,以获得type_info
提供的额外功能。
注意is_base_of
和derives_from
之间的对称性。实际上,你可以删除其中一个。
我建议你阅读this文章。