如果所有派生 类 在编译时已知,final 关键字是否提供优化?
Does the final keyword provide optimization if all derived classes are known at compile time?
理论上,在 C++11 及更高版本中的虚方法声明中使用 final
关键字允许方法的内联。
但是,我怀疑在实践中,如果在编译时知道没有派生的类覆盖那个方法,编译器可以为我们插入一个final
并内联这个方法.
However, putting this into compiler explorer shows that it still provides optimizations.
这是为什么?
优化方面,final
只影响编译器是否可以在编译时证明派生类型是什么。如果类型已知,则无需帮助编译器了解它。
final keyword on a virtual method declaration in C++11 and onwards allows inlining of the method
不是inlining
,而是去虚拟化。
However, I suspect that in practice, if it is known at compile time that there are no derived classes overriding that method, [..]
你无法在编译时知道没有其他派生的 classes(除非 class 是 final
):用户或其他 TU 可能会提供一些。它可能会在 link 完成,假设没有动态加载...
[..] the compiler can insert a final for us and inline the method
However, putting this into compiler explorer shows that it still provides optimizations.
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
Why is this?
这里,当c
的动态类型为C
时,编译器内联调用C::f
(rep ret
):
然后它不调用 C::f
,而是执行 C::f
所做的(-> 无)。
虚拟调用仍然是调用其他动态类型(jmp rax
)。
理论上,在 C++11 及更高版本中的虚方法声明中使用 final
关键字允许方法的内联。
但是,我怀疑在实践中,如果在编译时知道没有派生的类覆盖那个方法,编译器可以为我们插入一个final
并内联这个方法.
However, putting this into compiler explorer shows that it still provides optimizations.
这是为什么?
优化方面,final
只影响编译器是否可以在编译时证明派生类型是什么。如果类型已知,则无需帮助编译器了解它。
final keyword on a virtual method declaration in C++11 and onwards allows inlining of the method
不是inlining
,而是去虚拟化。
However, I suspect that in practice, if it is known at compile time that there are no derived classes overriding that method, [..]
你无法在编译时知道没有其他派生的 classes(除非 class 是 final
):用户或其他 TU 可能会提供一些。它可能会在 link 完成,假设没有动态加载...
[..] the compiler can insert a final for us and inline the method However, putting this into compiler explorer shows that it still provides optimizations.
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
Why is this?
这里,当c
的动态类型为C
时,编译器内联调用C::f
(rep ret
):
然后它不调用 C::f
,而是执行 C::f
所做的(-> 无)。
虚拟调用仍然是调用其他动态类型(jmp rax
)。