Search code examples
c++multiple-inheritanceambiguity

Why doesn't 'using A::X' avoid ambiguity with multiple inheritance?


This is similar to this question and this one, but I think (hope!) different enough to deserve an explanation.

I have a complex configuration framework, with decorator classes used to implement some common, simple actions (like flagging when a class Set accessor is called). I'm trying to introduce a new decorator (rather than a composition), which itself "should" inherit from that same common "Set-flagging" decorator.

I'm running into "ambiguous conversion from derived class to base class", and my attempts to work around it have failed. I'm missing something obvious, I'm sure.

Here's a very simple example, without all my framework stuff.

class A {
public:
  template <class T> void Set(T& arg, T val);
  bool Ready() const;
};

class B : private A {
  // Does stuff where I want to flag Set() actions
};

class C : public B, public A {
  // This class needs the B interface, and the Set()-flagging
public:
  void SetParam(double val) { Set(param, val); }

private:
  double param;
};

Note that I original used virtual inheritance of A, but in practice I need to keep the "Set-flag" for B distinct from the "Set-flag" for C, hence my attempt above.

The private inheritance above was my first attempt to avoid the ambiguity. I also tried introducing using directives in C:

class C : public B, public A {
  // This class needs the B interface, and the Set()-flagging
  using A::Set;
  using A::Ready;
};

This doesn't change the error. I understand from searching that the ambiguity is caught before the public/private state is checked. But I thought that the explicit using directives would resolve it. Why doesn't it? Do I need to go in and use A::Set(...) or A::Ready() explicitly everywhere?


Solution

  • Two solutions.

    If you really want to keep the private and multiple inheritance:

    class A {
    public:
        void Set() {};
        bool Ready() const {};
    };
    
    class B : private A {
    };
    
    class C : public B, public A {
    public:
        void SetParam() { C::A::Set(); }
    };
    

    Or if that is not needed, then a simpler one:

    class A {
    public:
        void Set() {};
        bool Ready() const {};
    };
    
    class B : public A {
    };
    
    class C : public B {
    public:
        void SetParam() { Set(); }
    };