Достаточно ли умны компиляторы, чтобы оптимизировать вызов Foo(), когда производный класс не переопределяет его?
struct Base {
virtual void Foo(int x) {}
};
struct DerivedA : Base {};
struct DerivedB : Base {
void Foo(int x) override { Bar(x); }
};
void Call(Base* b) {
b->Foo(42); // Is this optimized out for DerivedA?
}
int main() {
Base* a = new DerivedA();
Base* b = new DerivedB();
Call(a);
Call(b);
return 0;
}
Существует вероятность того, что другая единица компиляции представит производный класс с непустой реализацией Foo, поэтому приведенный здесь пример может не распространяться на полную программу, по крайней мере, без оптимизации времени компоновки или полной программы.
(Я исправил UB, так как этот вопрос не об этом.)
Оптимизация зависит от правила «как если» и «качества» компилятора, так что это зависит.
Оптимизированы ли пустые базовые виртуальные методы?
При достаточной видимости для компилятора вызовы могут быть полностью девиртуализированы. а затем пустую функцию можно удалить.
Окончательный исполняемый файл может быть просто
Bar(42);
Это зависит. Я только что вставил ваш пример в Проводник компилятора Godbolt. Мне пришлось удалить функцию void Bar(int)
, чтобы сделать ее компилируемой, и я добавил недостающие delete
s. Clang оптимизирует его с помощью «-O2», чтобы
Call(Base*): # @Call(Base*)
mov rax, qword ptr [rdi]
mov rax, qword ptr [rax]
mov esi, 42
jmp rax # TAILCALL
main: # @main
xor eax, eax
ret
в то время как gcc с тем же уровнем оптимизации генерирует вызов. Немного изменив ваш пример, чтобы использовать локальные объекты стека вместо двух новых объектов в куче, чтобы позволить gcc исключить вызов виртуальной функции.
Лучшее, что я могу сказать, это "Может быть". Он вернется к
Base::Foo
, и это ничего не даст. Правило «как если бы» должно быть в состоянии обрезать его. Хотя никаких гарантий.