Disclaimer: Compiler Optimization
This answer is about compiler optimization methods. Your compiler may or may not support them. Even if it supports the technique you are trying to use, they may not be available at your chosen optimization level.
Your mileage may vary.
Most derived class
A compiler can truly devirtualize a call if it knows that it is indeed the most derived class. How this can be achieved is discussed here . Example:
struct Base { virtual void call_me_virtual() = nullptr; }; struct Derived final : Base { void call_me_virtual() override { } }; void dosomething(Derived* d) { d->call_me_virtual(); }
Interestingly, if this is not done, you can always get someone else from your class in a different translation unit, so the compiler will not know about this "more derived" class in your current translation unit.
Another way to ensure that a class must be the most derived is to put it in an anonymous namespace:
struct Base { virtual void call_me_virtual() = nullptr; }; namespace { struct Derived : Base { void call_me_virtual() override { } }; void dosomething(Derived* d) { d->call_me_virtual(); } }
This works because Derived not known outside the current translation unit, which implies that any more derived class must be inside the current translation unit. However, this means that dosomething cannot be (correctly) called from outside the current compilation unit, so I also gave it an internal connection.
One of the exceptions to this rule are compilers, which can prove that an object arises from its representation, for example. if Derived is the most derived type in your current translation unit, and the object must always come from the current translation unit, then it cannot be from any derived type. The scope of this analysis can be expanded by optimizing the entire program.
Known type
A more common case is the fact that the compiler knows which type of object, for example, in both cases:
Derived d; d.call_me_virtual(); Base* b = new Derived; b->call_me_virtual();
The compiler can deduce that d.call_me_virtual and b->call_me_virtual will always resolve to Derived::call_me_virtual and thus Derived::call_me_virtual (and even embed) the call in this instance.
In the first case, this requires knowledge that an object of type Derived , in contrast to an object of a very similar type, Derived& , can only be a Derived object, and not something derived from it.
The second case requires static type analysis, which is performed by modern optimizing compilers. Seeing that b always initialized with a pointer to a Derived object, the compiler can prove which function will be called by b->call_me_virtual .
Completed Item Function
A similar case occurs when you complete one member function:
struct Base { virtual void call_me_virtual() = nullptr; }; struct Derived : Base { void call_me_virtual() override final { } }; void dosomething(Derived* d) { d->call_me_virtual(); }
While d can point to something derived from Derived , the call_me_virtual method call_me_virtual no longer be changed, so the compiler knows that Derived::call_me_virtual will always be called, and therefore can virtualize this call.