In the following code, it calls a virtual function foo via a pointer to a derived object. Will this call go through the vtable or will it call B::foo
directly?
If it goes via a vtable, what would be a C++ idiomatic way of making it call B::foo
directly? I know that in this case I am always pointing to a B
.
Class A
{
public:
virtual void foo() {}
};
class B : public A
{
public:
virtual void foo() {}
};
int main()
{
B* b = new B();
b->foo();
}
As usual, the answer to this question is "if it is important to you, take a look at the emitted code". This is what g++ produces with no optimisations selected:
which is using the vtable. A direct call, produced by code like:
looks like this:
Yes, it will use the vtable (only non-virtual methods bypass the vtable). To call
B::foo()
onb
directly, callb->B::foo()
.I changed the code up a bit to give it a go myself, and to me it looks like it's dropping the vtable, but I'm not expert enough in asm to tell. I'm sure some commentators will set me right though :)
I then converted this code to assembly like this:
And the interesting bits look to me like the 'opt' version is dropping the vtable. It looks like it's creating the vtable but not using it..
In the opt asm:
and the base.asm version of the same:
On line 93 we see in the comments:
_vptr.A
which I'm pretty sure means it's doing a vtable lookup, however, in the actual main function, it seems to be able to predict the answer and doesn't even call that useIt code:which I think is just saying, we know we're gonna return 2, lets just put it in eax. (I re ran the program asking it to return 200, and that line got updated as I would expect).
extra bit
So I complicated the program up a bit more:
In this version, the useIt code definitely uses the vtable in the optimized assembly:
This time, the main function inlines a copy of
useIt
, but does actually do the vtable lookup.What about c++11 and the 'final' keyword?
So I changed one line to:
and the compiler line to:
Thinking that telling the compiler that it is a final override, would allow it to skip the vtable maybe.
Turns out it still uses the vtable.
So my theoretical answer would be:
Most compilers will be smart enough to eliminate the indirect call in that scenario, if you have optimization enabled. But only because you just created the object and the compiler knows the dynamic type; there may be situations when you know the dynamic type and the compiler doesn't.
This is the compiled code from g++ (4.5) with -O3
The only optimization it did was that it knew which vtable to use (on the b object). Otherwise "call *_ZTV1B+16(%rip)" would have been "movq (%rax), %rax; call *(%rax)". So g++ is actually quite bad at optimizing virtual function calls.
Compiler can optimize away virtual dispatch and call virtual function directly or inline it if it can prove it's the same behavior. In the provided example, compiler will easily throw away every line of code, so all you'll get is this: