У меня есть дерево, где каждый узел в основном выглядит так:
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())
, но это потребовало бы, чтобы каждый набор в каждом узле никогда не был пустым.
Если я вас правильно понял, вы хотите тип 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
, На самом деле, вы можете удалить один из них.
Я предлагаю вам прочитать этот статья.
В любом случае я могу преобразовать
type_info*
к аргументу шаблона?
Нет, нет пути. Шаблоны — это время компиляции, RTTI — время выполнения. Нет связи между ними.
Я думаю, можно позвонить
decltype(*objects.begin())
, но это потребовало бы, чтобы каждый набор в каждом узле никогда не был пустым.
Это не потребует этого. decltype
не оценивает свои аргументы — это не нужно. Нужно только проверить типы. Вы можете счастливо сделать decltype(*a_null_pointer)
без вызова UB, так как выражение никогда не вычисляется — это так называемое недооцененный контекст. sizeof
принадлежит к той же категории.
Обратите внимание, что это не слишком дорого для вас — вы просто получите object*&
назад. Обычно вы не можете получить тип из информации времени выполнения без предварительной подготовки отображения.
is_base_of
а также decltype
являются чисто компилируемыми временными конструкциями; они не будут отражать динамический тип ваших объектов.
type_info
не предоставляет возможности для проверки отношений подкласса; единственный способ выполнить эту проверку во время выполнения — использовать dynamic_cast
, Вам нужно будет хранить unique_ptr<base>
и посмотрим, сможет ли он dynamic_cast
к производному типу.