In general, you can trust your compiler optimizer to make a good choice, depending on the optimization settings.
To prove the concept, here we use code that uses different cases, Foo and Bar , as you did:
struct Tzar : public Foo { void DoSomething() override final;
You can find here the assembly code generated for all cases that you submitted using GCC 5.3.0 without optimization. It is colored to help you see the assembler code for each C ++ statement.
The first call will always be a direct call:
lea rax, [rbp-80] ; take the object pointer from the stack mov rdi, rax ; set the this pointer of the invoking object call Foo::DoSomething() ; direct call to the function
Without optimization, all other DoSomething() calls will use an indirect call. here's an example b->DoSomething() :
mov rax, QWORD PTR [rbp-32] mov rax, QWORD PTR [rax] mov rax, QWORD PTR [rax] ; load the function call from the vtable mov rdx, QWORD PTR [rbp-32] mov rdi, rax ; set the this pointer of the invoking object call rax ; indirect call via register
If you now set the -O2 optimization flag to the compiler options, you will see that most indirect calls are optimized when the compiler can predict the actual type of polymorphic pointer. In the above example, this would be:
mov rdi, rax ; set the this pointer of the invoking object call Bar::DoSomething() ; direct call !!
When the compiler cannot safely predict the real type, it will use an indirect call. For example, if you have a function bar_factory() that returns a Bar pointer, the compiler cannot know whether it will return a pointer to a Bar object or a class object derived from Bar (which can be defined in another compilation unit and are not known here).
The only unexpected thing is the definition of a virtual function as a final override (the Tzar class in my example). Here you can expect the compiler to take advantage of the fact that DoSomething() should not get further results. But this is optional.