Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ambiguity in multiple inheritance of interfaces in C++

I made a test code as following:

#include <iostream>
using namespace std;

#ifndef interface
#define interface struct
#endif

interface Base
{
    virtual void funcBase() = 0;
};

interface Derived1 : public Base
{
    virtual void funcDerived1() = 0;
};

interface Derived2 : public Base
{
    virtual void funcDerived2() = 0;
};

interface DDerived : public Derived1, public Derived2
{
    virtual void funcDDerived() = 0;
};

class Implementation : public DDerived
{
public:
    void funcBase() { cout << "base" << endl; }
    void funcDerived1() { cout << "derived1" << endl; }
    void funcDerived2() { cout << "derived2" << endl; }
    void funcDDerived() { cout << "dderived" << endl; }
};

int main()
{
    DDerived *pObject = new Implementation;
    pObject->funcBase();

    return 0;
}

The reason I wrote this code is to test if the function funcBase() can be called in an instance of DDerived or not. My C++ complier (Visual Studio 2010) gave me a compile error message when I tried to compile this code. In my opinion, there is no problem in this code because it is certain that the function funcBase() will be implemented (thus overriden) in some derived class of the interface DDerived, because it is pure virtual. In other words, any pointer variable of type Implementation * should be associated with an instance of a class deriving Implentation and overriding the function funcBase().

My question is, why the compiler give me such an error message? Why the C++ syntax is defined like that; i.e., to treat this case as an error? How can I make the code runs? I want to allow multiple inheritance of interfaces. Of course, if I use "virtual public" or re-declare the function funcBase() in Implementation like

interface DDerived : public Derived1, public Derived2
{
    virtual void funcBase() = 0;
    virtual void funcDDerived() = 0;
};

then everything runs with no problem.

But I don't want to do that and looking for more convenient method, because virtual inheritance may degrade the performance, and re-declaration is so tedious to do if inheritance relations of classes are very complex. Is there any methods to enable multiple inheritance of interfaces in C++ other than using virtual inheritance?

like image 666
user1361349 Avatar asked Apr 27 '12 15:04

user1361349


4 Answers

As you've defined it, your object structure looks like this:

enter image description here

The important point here is that each instance of Implementation contains two entirely separate instances of Base. You're providing an override of Base::funcBase, but it doesn't know whether you're trying to override funcBase for the Base you inherited through Derived1, or the Base you inherited through Derived2.

Yes, the clean way to deal with this is virtual inheritance. This will change your structure so there's only one instance of Base:

enter image description here

This is almost undoubtedly what you really want. Yes, it got a reputation for performance problems in the days of primitive compilers and 25 MHz 486's and such. With a modern compiler and processor, you're unlikely to encounter a problem.

Another possibility would be some sort of template-based alternative, but that tends to pervade the rest of your code -- i.e., instead of passing a Base *, you write a template that will work with anything that provides functions A, B, and C, then pass (the equivalent of) Implementation as a template parameter.

like image 137
Jerry Coffin Avatar answered Oct 24 '22 12:10

Jerry Coffin


The C++ language is designed in such a way that in your first approach without virtual inheritance there will be two parent copies of the method and it can't figure out which one to call.

Virtual inheritance is the C++ solution to inheriting the same function from multiple bases, so I would suggest just using that approach.

Alternately have you considered just not inheriting the same function from multiple bases? Do you really have a derived class that you need to be able to treat as Derived1 or Derived2 OR Base depending on the context?

In this case elaborating on a concrete problem rather than a contrived example may help provide a better design.

like image 32
Mark B Avatar answered Oct 24 '22 13:10

Mark B


DDerived *pObject = new Implementation;
pObject->funcBase();

This creates a pointer of type DDerived to a Implementation. When you are using DDerived you really just have a pointer to an interface.

DDerived does not know about the implementation of funcBase because of the ambiguity of having funcBase being defined in both Derived1 and Derived2.

This has created a inheritance diamond which is what is really causing the problem.

http://en.wikipedia.org/wiki/Diamond_problem

I also had to check on the interface "keyword" you have in there

it's an ms-specific extension that's recognised by visual studio

like image 39
nate_weldon Avatar answered Oct 24 '22 14:10

nate_weldon


I think C++ Standard 10.1.4 - 10.1.5 can help you to understand the problem in your code.

class L { public: int next; /∗ ... ∗/ };
class A : public L { /∗...∗/ };
class B : public L { /∗...∗/ };
class C : public A, public B { void f(); /∗ ... ∗/ };

10.1.4 A base class specifier that does not contain the keyword virtual, specifies a non-virtual base class. A base class specifier that contains the keyword virtual, specifies a virtual base class. For each distinct occurrence of a non-virtual base class in the class lattice of the most derived class, the most derived object (1.8) shall contain a corresponding distinct base class subobject of that type. For each distinct base class that is specified virtual, the most derived object shall contain a single base class subobject of that type. [ Example: for an object of class type C, each distinct occurrence of a (non-virtual) base class L in the class lattice of C corresponds one-to-one with a distinct L subobject within the object of type C. Given the class C defined above, an object of class C will have two subobjects of class L as shown below.

10.1.5 In such lattices, explicit qualification can be used to specify which subobject is meant. The body of function C::f could refer to the member next of each L subobject: void C::f() { A::next = B::next; } // well-formed. Without the A:: or B:: qualifiers, the definition of C::f above would be ill-formed because of ambiguity

So just add qualifiers when calling pObject->funcBase() or solve ambiguity in another way.

pObject->Derived1::funcBase();

Updated: Also very helpful reading will be 10.3 Virtual Functions of Standard.

Have a nice weekend :)

like image 1
SVGreg Avatar answered Oct 24 '22 13:10

SVGreg