Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ambiguous inheritance of abstract base classes:

Tags:

c++

I have a far more complicated class structure than this, but boiling the problem down to its essence, this describes my scenario: I have two classes, A & B, that implement pure virtual base classes that share a common ancestor, and then a third class C that com-posits A & B. Finally, a template class that fills in the common methods in the pure virtual base:

struct I {
  virtual void r()=0;
};

struct A : I {};
struct B : I {};

struct C : A, B {
  void q(){
    r();              // the problem is here.
  }
};

struct D : C {
  virtual void r(){
  }
};

C* c = new D;
c->q();

My problem is, I can't see any way to get C::q to call r().

void C::q(){
  r();    // is ambiguous
  A::r(); // is pure virtual
  B::r(); // also is pure virtual
  D::r(); // C doesn't know about D
  ((D*)this)->r(); // is dubious and requires C to know about D.
}

How can I call the r() method from C so that the correct virtual method is called?


Sorry, I should have clarified that virtual inheritance can't be used here. I have found two solutions:

struct C : A, B {
  virtual void r()=0;
  ...

OR

struct C : A, B {
   using A::r;
   ...

Both appear to disambiguate the call to r() sufficiently for everything to resolve.

like image 207
Chris Becke Avatar asked Nov 26 '10 08:11

Chris Becke


4 Answers

Redeclare method r as pure virtual in C:

struct C : A, B {
  void q(){
    r();              // the problem is here.
  }

  virtual void r()=0;
};
like image 67
Cătălin Pitiș Avatar answered Oct 22 '22 12:10

Cătălin Pitiș


Try virtual inheritance

struct A : virtual  I {};
struct B : virtual I {};
like image 24
T33C Avatar answered Oct 22 '22 12:10

T33C


Tell the compiler which part of the hierarchy to follow:

struct C : A, B {
  void q(){
    A * p = this;
    p->r();              // recent GCC compiles this
  }
};
like image 2
dennycrane Avatar answered Oct 22 '22 11:10

dennycrane


It's ambiguous because the compiler doesn't know which r() to call, the one coming from A or the one coming from B.

The easy way is to write:

static_cast<A*>(this)->r();

or

static_cast<B*>(this)->r();

But I think that none of this is the answer you're looking for. You clean up the situation by inheriting through virtual the interface I:

struct A : virtual I {};
struct B : virtual I {};

Now, you can call

void C::q() { r(); }

as you would expect. The simple explanation of this is that, by using virtual, class C gets only one "copy" of the interface I, not two. This disambiguates your code.

like image 2
Simone Avatar answered Oct 22 '22 13:10

Simone