Search code examples
c++polymorphismmultiple-inheritance

Multiple Inheritance Virtual Call Ambiguity


The problem at hand is hard to describe so the code is placed up front for better clarity.

struct Base
{
    int b;
    virtual void foo(){cout << b << endl;}
    Base(int x) : b(x){}
};

struct Derived1 : Base    //not virtual
{
    virtual void foo(){/*Derived2's code*/}
    Derived1() : Base(1){}
};

struct Derived2 : Base    //not virtual
{
    virtual void foo(){/*Derived2's code*/}
    Derived2() : Base(2){}
};

struct MultiInheritance : Derived1, Derived2
{
    void bar1()
    {
        //needs to access Derived1's Base foo()
    }
    void bar2()
    {
        //needs to access Derived2's Base foo()
    }
};

Suppose that in some weird bizarre scenario, I would want a base class MultiInheritance that has two base classes Derived1 and Derived2 that have a common non-virtual base class Base.

There are two Base in MultiInheritance, how do I specify which Base class I wish to access in MultiInheritance?

The code above seems to work fine by casting several times, but I'm not sure if this is defined behavior or not. If it is, how is this implemented by the compiler to fulfill the needs of polymorphism? On one hand virtual calls should all result in the same virtual function table, but on the other if it does it wouldn't output different answers.

EDIT

I wish to emphasize that the Base classes are required to be non-virtual

EDIT2

Deep apologies, I seriously misrepresented myself. The code above is updated better reflects my original question.


Solution

  • Here's a more illustrative example.

    #include <iostream>
    using namespace std;
    
    template <typename Res, typename Arg>
    Res& as(Arg& arg)
    {
      return arg;
    }
    
    struct Base
    {
        virtual void foo() = 0;
    };
    
    struct Derived1 : Base {};
    struct Derived2 : Base {};
    
    struct MoreDerived1 : Derived1
    {
        void foo() { cout << "Derived1\n"; }
    };
    
    struct MoreDerived2 : Derived2
    {
        void foo() { cout << "Derived2\n"; }
    };
    
    struct MultiInheritance : MoreDerived1,  MoreDerived2
    {
        void bar1() { as<Derived1>(*this).foo(); }
        void bar2() { as<Derived2>(*this).foo(); }
    };
    
    int main ()
    {
         MultiInheritance m;
         m.bar1();
         m.bar2();
    }
    

    This example illustrates that:

    1. You don't need to specify which Base you need explicitly using a full inheritance path, it is enough to go down to a subobject that has an unambiguous Base subobject
    2. The virtual function mechanism works here. It would not work if you tried to call Derived1::foo().

    The as helper function is just a syntactic sugar, you could just as well say

    Derived1& d = *this;
    d.foo();