Я согласен с m-sharp, что вы не избежите полиморфизма во время выполнения.
Если вы цените оптимизацию выше элегантности, попробуйте заменить скажем
void invoke_trivial_on_all(const std::vector<Base*>& v)
{
for (int i=0;i<v.size();i++)
v[i]->trivial_virtual_method();
}
с чем-то вроде
void invoke_trivial_on_all(const std::vector<Base*>& v)
{
for (int i=0;i<v.size();i++)
{
if (v[i]->tag==FooTag)
static_cast<Foo*>(v[i])->Foo::trivial_virtual_method();
else if (v[i]->tag==BarTag)
static_cast<Bar*>(v[i])->Bar::trivial_virtual_method();
else...
}
}
это не красиво, конечно, не ООП (скорее, возвращение к тому, что вы могли бы сделать в старом добром 'C'), но если виртуальные методы достаточно тривиальны, вы должны получить функцию без вызовов (при условии достаточно хорошего компилятора и оптимизации опции). Вариант с использованием dynamic_cast или typeid может быть немного более элегантным / безопасным, но имейте в виду, что эти функции имеют свои собственные издержки, которые в любом случае, вероятно, сопоставимы с виртуальным вызовом.
Скорее всего, вы увидите улучшение по сравнению с вышеописанным, если некоторые методы классов не используются, и это избавило вас от их вызова, или если функции содержат общий код, инвариантный к циклам, и оптимизатору удается его поднять. из цикла.