Search code examples
c++inheritancemultiple-inheritancevirtual-inheritancemsvc12

Ambiguous inheritance of function when multiple inheritance of classes that themselves have diamond inheritance in their hierarchy


Word description (code below): I have a library that provides a collection of classes. For each group of class we have two concrete types, (ClassA_Partial, ClassA), (ClassB_Partial, ClassB) etc. Each of these implements (Interface_Partial, Interface) respectively. Additionally, Interface is a Interface_Partial and each Class? is a Class?_Partial - creating a diamond inheritance pattern where the top is inherited virtually.

Why areInterface_Partial functions ambiguous when inheriting both ClassA and ClassB?

struct Interface_Partial
{ 
    virtual ~Interface_Partial(); 
    virtual void f() = 0;
};

struct Interface
    :
    virtual Interface_Partial
{
    virtual void g() = 0;
};


struct ClassA_Partial : public virtual Interface_Partial
{
    void f() {};
};


struct ClassA : public Interface, public virtual ClassA_Partial
{
    void g() {};
};

struct ClassB_Partial : public virtual Interface_Partial
{
    void f() {};
};


struct ClassB : public Interface, public virtual ClassB_Partial
{
    void g() {};
};

struct MyClass : public ClassA, public ClassB
{ }; // error C2250: MyClass : ambiguous inheritance of 'void Interface_Partial::f(void)'

Why can't we disambiguate the way we usually do when we inherit a common interface more than once? For example

struct ClassX : public Interface_Partial { void f() {} };
struct ClassY : public Interface_Partial { void f() {} };
class Another : public ClassX, public ClassY
{};

void func()
{
    // This is ok
    Another a;
    a.ClassX::f();   

    // Why would this not work?
    // unambiguously refers to the one and only f() function 
    // inherited via  ClassA
    MyClass b;
    b.ClassA::f();   
}

Solution

  • Because of the virtual inheritance, there is only one vtable for the base class Interface_Partial - once you use virtual inheritance, the "virtualness" infects all derived classes at all levels

    The inheritance is ambigous because MyClass has two different version of f() available - one from ClassA and one from ClassB. Because of the virtual inheritance of Interface_Partial, you have two derived-class implementations that are at the same level and are trying to override the same virtual function. Declaring a virtual base class makes all derived classes share the virtual base class, including its vtable. The shared vtable gets updated to contain the pointer of the virtual function which should be called. But since there are two equally "good" ones to choose from, there is no way to pick one over the other.

    In the other example you give, Interface_Partial is a non-virtual base class for ClassX and ClassY, so each class is overriding a completely different virtual function. This is unambigous to the compiler, although when you call them, you have to specify which specific f() you want to call.

    You can resolve this by providing an implementation of f() in MyClass.