1

while trying to analyse in greater depth inheritance mechanism of C++ I stumbled upon the following example:

#include<iostream>

using namespace std;

class Base {
public:
    virtual void f(){
    cout << "Base.f" << endl; 
    }
};

class Left : public Base { //NOT VIRTUAL!!!
public:
void g(){ 
        f();
    }     
};

class Right : public Base{
public:
    virtual void f(){
    cout << "Right.f" << endl; 
    }
};

class Bottom : public Left, public Right{
public:
    Bottom(int arg){ }
    //void f() { }
};

int main(int argc,char **argv)
{
    Bottom* b = new Bottom(23);
    b->g();
}

It is clear that calling

b->f()

is ambiguous, so there is no unique method f() on object Bottom. Now, calling

b->g()

works fine and prints

Base.f

Well, as far as I see this:

  1. static type is Bottom, so we call its g() method, as it is non-virtual
  2. g() method is inherited from Left, so we call this inherited method
  3. Now, g() in Left tries to call virtual method f(). According to C++ sepcification, we call f() method of a dynamic type of our pointer (which is Bottom)

BUT Bottom does not have method f()... at least not a unique one. Why does this program executes Left::Base::f() rather than Right::Base::f() or why does it simply not states that call to f() is ambiguous from Bottom?

Rafał Rawicki
  • 22,324
  • 5
  • 59
  • 79
Bober02
  • 15,034
  • 31
  • 92
  • 178
  • When you declared function as virtual it will be virtual all time – Denis Ermolin Apr 15 '12 at 21:50
  • And how does that relate to the question at hand? I know we need to call dynamiic type's method. Besides method can be hidden as well, si you might hide virtual method by simply rewriting it with same signature but making it non-virtual I beelieve – Bober02 Apr 15 '12 at 22:05

2 Answers2

2

The short answer is that (as you noted), Bottom does not have method f(), so there is no need in trying to call it.

Bottom contains the two subobjects Left and Right. Each of them inherits from Base, so Bottom contains the member functions Left::f() and Right::f(), but no Bottom::f(). Since Bottom does not override Left::f() (using Right::f() for example), Base::f() is the unique final overrider in Left::g().

cf. the example in 10.3.9 from the C++03 standard.

Tobias
  • 6,388
  • 4
  • 39
  • 64
  • This is completely not what I asked... The spec of C++ says to call dynamic type's method, but here, the dynamic type does not have f() at all... so why do we call anything? – Bober02 Apr 15 '12 at 21:41
  • 1
    @Bober02: The dynamic type actually has *two* `f()` one in each side of the hierarchy. Note that what the standard says is that the *final overrider* of the function will be called, and that is the key point. While there are two `f`, there is a single overrider for `Left::Base::f` – David Rodríguez - dribeas Apr 15 '12 at 21:59
1

Since there is no virtual inheritance there are two copies of the Base object in your Bottom object. But if you move up the hierarchy to Left where g() is defined, there is a single Base subobject and that is the one being called. Since it is not overridden in Left (or Bottom) it will call the Base version. Note that Right::f() only overrides f() for it's own Base subobject.

Calling f directly on Bottom is ambiguous, as there is no f() in Bottom it will try to look in it's bases and will find Left::Base::f and Right::Base::f and the compiler does not know which of the two to use.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • Thanks for your answer and comment. Final overrider... Damn it is complicated. Is there any good source on multiple virtual/non virrtual inheritance that would explain edge cases such as this? I can't find any well written one... – Bober02 Apr 15 '12 at 22:09
  • @Bober02 It is not *that* complicated. Just think on the objects that exists: because inheritance is not virtual you have two `Base` subobjects. Once you are able to identify the objects it is simple to follow the chain of overrides, with the only *strange* behavior in that a single method can override multiple virtual functions (as many as one per base) – David Rodríguez - dribeas Apr 15 '12 at 22:13
  • What do you mean one method can override multiple virtual functions? Could you give an example? Btw, what is the best resource on advanced topics such as this in your opinion? Thanks – Bober02 Apr 15 '12 at 22:16
  • @Bober02: The best resource is experience, and reading a lot everywhere. As of the example: `struct A { virtual void f() {} }; struct B { virtual void f() {} }; struct C : A, B { virtual void f() {} };` `C::f` is the final overrider for both `A::f` and `B::f`. The signatures must match, but a single function can override more than one virtual function (as per the example) – David Rodríguez - dribeas Apr 15 '12 at 22:56
  • Reading everywhere... That's pretyt big statement. Any good books on advanced topics maybe? BTW, would you mind having a look at this question which I posted just now? thanks: http://stackoverflow.com/questions/10166926/virtual-multiple-inheritance-final-overrider – Bober02 Apr 15 '12 at 23:03
  • @Bober02: That is how I learnt it. I have been recommended a couple of times *Inside the C++ Object Model* by Lippman, but I have not read it. I assume that it is probably a good read regarding all this details. – David Rodríguez - dribeas Apr 15 '12 at 23:53