Suppose you have this simple class hierarchy:
struct base {
virtual void f () const = 0;
};
struct derived : public base {
virtual void f () const final
{
...
}
};
There is only one implementation of f(), and because it is declared final, may we consider that there is no polymorphism?
If so, will the compiler optimize code by avoiding use of a virtual table since 'You don't pay what you don't use?'
Thank you.
There is a polymorphism because there is a virtual function and you may define other derived classes from the base class.
From the C++ 17 Standard (13.3 Virtual functions)
1 [ Note: Virtual functions support dynamic binding and object-oriented programming. — end note ] A class that declares or inherits a virtual function is called a polymorphic class.
will the compiler optimize code by avoiding use of a virtual table
If the compiler has a pDerived->f()
then yes, this is what it typically will do because this is what final
is designed for in the first place.
If it has a pBase->f()
, then such optimisation is only possible if the compiler can prove that pBase
points to a derived
and not any other class derived from base
. f
being or not being declared final
is irrelevant for this analysis. Note that derived classes can come from different translation units. The compiler normally only sees one translation unit at a time, so it needs some kind of data flow analysis to eliminate this possibility. Link-time optimisations do not really help here because additional modules can be loaded at run time, and the link time optimiser cannot see them.
I made an example in compiler explorer: https://godbolt.org/z/exsx8dzra
The code:
struct base {
virtual int f () const = 0;
};
struct derived : public base {
virtual int f () const final { return 2; }
};
int returnf(const base& b)
{
return b.f();
}
The Assembly (x86-64 gcc12.2, -std=c++20 -O3):
main:
xor eax,eax
ret
cs nop WORD PTR [rax+rax*1+0x0]
nop DWORD PTR [rax]
returnf(base const&):
mov rax,QWORD PTR [rdi]
mov rax,QWORD PTR [rax]
cmp rax,0x401140
jne 401138 <returnf(base const&)+0x18>
mov eax,0x2
ret
nop DWORD PTR [rax+0x0]
jmp rax
nop WORD PTR [rax+rax*1+0x0]
derived::f() const:
mov eax,0x2
ret
cs nop WORD PTR [rax+rax*1+0x0]
Here you can see in the assembly that the virtual function call is not completely optimized away, namely there still is a check if the object is of the type derived:
cmp rax,0x401140
jne 401138 <returnf(base const&)+0x18>
If so it returns the value specified in the method, otherwise it jumps to rax
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.