Как говорит Xeo, вам, вероятно, не следует делать это в данном конкретном случае - существуют лучшие варианты дизайна. Тем не менее, вы можете сделать это с помощью RTTI, но это, как правило, вызывает недовольство, поскольку ваш process()
становится централизованной точкой обслуживания, которую необходимо обновлять по мере добавления новых производных классов. Это легко упустить из виду и подвержено ошибкам во время выполнения.
Если вы по какой-то причине должны продолжить это, то, по крайней мере, обобщите средство, чтобы одна функция использовала определение типа среды выполнения на основе RTTI для вызова произвольного поведения, как в:
#include <iostream>
#include <stdexcept>
struct Base
{
virtual ~Base() { }
template <class Op>
void for_rt_type(Op& op);
};
struct Derived1 : Base
{
void f() { std::cout << "Derived1::f()\n"; }
};
struct Derived2 : Base
{
void f() { std::cout << "Derived2::f()\n"; }
};
template <class Op>
void Base::for_rt_type(Op& op)
{
if (Derived1* p = dynamic_cast<Derived1*>(this))
op(p);
else if (Derived2* p = dynamic_cast<Derived2*>(this))
op(p);
else
throw std::runtime_error("unmatched dynamic type");
}
struct Op
{
template <typename T>
void operator()(T* p)
{
p->f();
}
};
int main()
{
Derived1 d1;
Derived2 d2;
Base* p1 = &d1;
Base* p2 = &d2;
Op op;
p1->for_rt_type(op);
p2->for_rt_type(op);
}
В приведенном выше коде вы можете заменить свою собственную операционную функцию и выполнить такую же передачу времени от времени исполнения до компиляции. Это может или не может помочь думать об этом как о заводском методе в обратном порядке: -}.
Как уже говорилось, for_rt_type
необходимо обновлять для каждого производного типа: особенно больно, если одна команда «владеет» базовым классом, а другие команды пишут производные классы. Как и в случае со многими хакерскими вещами, он более практичен и удобен для поддержки частной реализации, а не как функция API низкоуровневой корпоративной библиотеки. Желание использовать это все еще обычно признак плохого дизайна в другом месте, но не всегда: иногда существуют алгоритмы (Op
s), которые приносят огромную пользу:
- оптимизация во время компиляции, удаление мертвого кода и т. Д.
- производным типам нужна только одна и та же семантика, но детали могут различаться
- например.
Derived1::value_type
is int
, Derived2::value_type
is double
- позволяет алгоритмам для каждого быть эффективными и использовать соответствующее округление и т. Д. Аналогично для различных типов контейнеров, где используется только общий API.
- вы можете использовать метапрограммирование шаблонов, SFINAE и т. Д. Для настройки поведения специфичным для производного типа способом
Лично я считаю, что знание и умение применять эту технику (хотя и редко) является важной частью овладения полиморфизмом.