简体   繁体   中英

Virtual C++ method with no polymorphism

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM