конечное ключевое слово в объявлении виртуального метода в C ++ 11 и далее позволяет встраивать метод
Не inlining
, но де-виртуализация.
Однако я подозреваю, что на практике, если во время компиляции известно, что нет никаких производных классов, переопределяющих этот метод, [..]
Вы не можете знать во время компиляции, что естьникакие другие производные классы (если класс не final
): пользователь или другой TU может предоставить некоторые. потенциально это может быть сделано по ссылке, при условии отсутствия динамической загрузки ...
[..] компилятор может вставить финал для нас и встроить метод. Однако, добавив это в проводник компилятора, вы увидите, что онпо-прежнему обеспечивает оптимизацию.
struct A {
virtual void f() {}
};
struct B : public A {
void f() final override {}
};
struct C : public A {
void f() override {}
};
void foo(B& b) { b.f(); }
void bar(C& c) { c.f(); }
C::f():
rep ret
foo(B&):
rep ret
bar(C&):
mov rax, QWORD PTR [rdi]
mov rax, QWORD PTR [rax]
cmp rax, OFFSET FLAT:C::f()
jne .L6
rep ret
.L6:
jmp rax
Почему это так?
Здесь компилятор встраивает вызов C::f
(rep ret
) для случая, когда динамический типc
- это C
: тогда вместо вызова C::f
он делает то, что делает C::f
(-> ничего).
виртуальный вызов все еще является вызовом для другого динамического типа (jmp rax
).