0

I am facing inheritance hierarchy error which I root caused to this code snippet. The definition of l() is defined in Template class Y, I don't understand why is the compiler finding it ambiguous.Class Z ptr_ is pointing to class Y object which has the definition of l() isn't this how it should work ?

Unfortunately the base classes are all part of a static library, class C is the only thing under my control . I am not sure what I can do here.

Error:
Line 84: Char 13: error: non-static member 'l' found in multiple base-class subobjects of type 'D':
    class C -> class A -> class D
    class C -> class B -> class D
      ptr_->l ()

Line 96: Char 15: note: in instantiation of member function 'Z<C>::~Z' requested here
  Z < C > t = new Y < C > ();
              ^

Line 31: Char 16: note: member found by ambiguous name lookup
  virtual void l () = 0;
#include <iostream>

using namespace std;

template <class T> 
class Y : public T {
public:
  void l() {}
  void p() {}
};

class D {
public:
  virtual void l() = 0;
  virtual void p() = 0;
};

class A : public D {
public:
  virtual void f(int a) = 0;
  virtual void d() = 0;
};

class B : public D {
public:
  virtual void j(string x) = 0;
  virtual void d() = 0;
};

class C : public A, public B {
public:
  void f(int a) override {}

  void j(string x) override {}

  void d() override {}
};

template <class T> 
class Z {
public:
  Z(T *ptr) : ptr_(ptr) {}

  ~Z() {
    if (ptr_)
      ptr_->l();
  }

protected:
  T *ptr_;
};

int main() {

  Z<C> t = new Y<C>();

  return 0;
}


  • 1
    `class C` inherits from `class A` and `class B`. Thus, it exposes two `virtual` methods `l()`: `A::l()` and `B::l()`. Hence, the compile cannot decide which one to choose for `ptr_->l();`. Either use _virtual_ base classes or help it (by inserting a cast) to choose the intended. – Scheff's Cat Sep 16 '20 at 07:04
  • 1
    It's because `C` has inherited a member function `l()` via two distinct paths (as `A::l()` and `B::l()`). The fact that the two both resolve to `D::l()` is irrelevant - there are two distinct paths by which it could be reached, and no reason to prefer one path or the other, and nothing preventing either `A` or `B` from each providing their own overrides. Options to fix (short of fixing the inheritance relationships to eliminate the ambiguity, which is often preferable) are to name which one you intend (e.g. `ptr->A::l()` or `ptr->B::l()`) or make `D` a virtual base of both `A` and of `B`. – Peter Sep 16 '20 at 07:04
  • 1
    FYI: [SO: In C++, what is a virtual base class?](https://stackoverflow.com/a/21607/7478597) – Scheff's Cat Sep 16 '20 at 07:06
  • Unfortunately the base classes are all part of a static library, class C is the only thing under my control . I am not sure what I can do here. – Punit Salian Sep 16 '20 at 07:10

1 Answers1

1

Your problem is that you have an inheritance hierarchy that looks like this:

  C
 / \
A   B
|   |
D   D   <- Defines l()

So when you call l() through a C type, then the compiler don't know if you want to call C::A::D::l() or C::B::D::l().

One way to fix this is to specify which branch you actually want to use in the C class.

The compiler complains in your example because l() is abstract in D, however if that isn't the case in your "real" code, then this works to fix the ambiguity:

class C : public A, public B {
public:
    ...
    virtual void l() override { A::l(); } // Or B::l() if you prefer, of course
};

But if l() is abstract in D, and isn't implemented in A or B, then you can do this in C:

class C : public A, public B {
public:
    ...
    virtual void l() = 0;
};
Frodyne
  • 3,547
  • 6
  • 16
  • Small addendum to this: If you can design your system to work without multiple inheritance, then you should seriously consider doing that. Multiple inheritance is a powerful tool, but it is also pretty complicated behind the scenes, and it is easy to bump into weird situations where stuff just don't work like you expect. – Frodyne Sep 16 '20 at 07:51