2

Considering this simple example:

class Base {
    virtual void foo() {};  
};

class Derived: public Base {
    void foo() {}; 
}; 

Base    *b = new Derived;
Derived *d = new Derived;
b->foo();
d->foo();

My question is: does a call to a member function declared as virtual in a base class (but not in the derived class) through a derived class pointer uses (and pay the price for) the vtable mechanism ? In the example b->foo() uses the vtable mechanism to call the Derived::foo(), but d->foo()?

If yes, how circumvent this behavior: when using explicitly a Derived pointer, I would like to directly call the Derived::foo() method without paying the cost of the vtable, as if the base class does not exist?

YSC
  • 38,212
  • 9
  • 96
  • 149
janou195
  • 1,175
  • 2
  • 10
  • 25
  • 1
    What is your *actual* problem? *Why* do you want to sidestep the vtable lookup? And have you *measured* that this is a serious bottleneck in your application? It's not just premature optimization because of something you heard or read? – Some programmer dude Sep 29 '17 at 09:09
  • 2
    If you want to be sure that classes inheriting from `Derived` does not override `foo`, you can use the [`final`](http://en.cppreference.com/w/cpp/language/final) keyword. If you do so, the compiler will probably optimise out the vtable lookup when calling `foo()` on a `Derived*`. – Holt Sep 29 '17 at 09:10
  • I thought about this final keyword, but how to check if the optimizer actually skip the vtable? – janou195 Sep 29 '17 at 09:22
  • 1
    Compilers try to optimize virtual dispatch and remove the indirection if possible. Gcc has been much improved in this direction. You can read a lot about it in a great series of articles from one of the gcc developers: https://hubicka.blogspot.de/2014/01/devirtualization-in-c-part-1.html – Jens Sep 29 '17 at 09:22
  • If you are doing this is an optimization, I really hope that you have measured thoruoughly to prove that is a hotspot. If not, I would really advice to profile before doing anything like this. – Jens Sep 29 '17 at 09:40
  • 1
    "I thought about this final keyword, but how to check if the optimizer actually skip the vtable?" Check out the generated assembly code for this. – geza Sep 29 '17 at 10:03
  • What is your real code? – curiousguy Oct 01 '17 at 12:35

2 Answers2

7

The syntax

d->Derived::foo();

will suppress virtual dispatch and guarantee that the function Derived::foo is the one called, even in the presence of overriding functions.

This is rarely what you want, but I cannot suggest alternative solutions unless you explain why you're trying to do this. For example, in the code snippet given, there is no reason why the Derived should be dynamically allocated at all. You could just write this instead:

Derived d;
d.foo();

Here, the compiler knows for sure that the dynamic type is Derived, so no virtual dispatch is needed.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312
2

does a call to a member function declared as virtual in a base class (but not in the deverived class)...

I've stopped to read here, because if Base declare foo as virtual, Derived can only override foo, Derived::foo (supposedly signature match) is implicitly virtual.

The following definitions of Derived::foo are identical:

class Derived: public Base {
  void foo() {}; 
};

class Derived: public Base {
  virtual void foo() {}; 
};

This mechanism is called implicit virtual propagation, and this SO answer tries and explain why id was conceived that way. Here is the rationale about it:

10.3 Virtual functions

2 If a virtual member function vf is declared in a class Base and in a class Derived, derived directly or indirectly from Base, a member function vf with the same name, parameter-type-list (8.3.5), cv-qualification, and ref-qualifier (or absence of same) as Base::vf is declared, then Derived::vf is also virtual (whether or not it is so declared) and it overrides Base::vf.

Community
  • 1
  • 1
YSC
  • 38,212
  • 9
  • 96
  • 149
  • Ok, so my question now is: how to circumvent that? – janou195 Sep 29 '17 at 09:21
  • 1
    @janou195 You circumvent it using the trick shown in Brian's answer. But this would no longer respect the expected semantics nor the Open/Closed principle: If `d` would point to an object of `class TwiceDerived : public Derived` with is own special function implementation you would ignore this specialization and still call `Derived::foo()`. Sooner or later, this will create havoc (e.g. if `Derived::foo()` and `TwiceDerived::foo()`are using different assumptions to downcast member pointers of other types). If you're so concerned about performance, use non virtual members instead – Christophe Sep 29 '17 at 18:04