'Inaccessible direct base' caused by multiple inheritance

This has nothing to do with overriding functions. It has to do with conversions. It really doesn't have to do with accessibility (i.e "private" or such) directly either. Here is a simpler example

struct A { int a; };
struct B : A { };
struct C : B, A { }; // direct A can't be referred to!

You can refer to the indirect A object by first converting to B and then to A:

B *b = &somec;
A *a = b;

You cannot do such with the direct A object. If you try to directly convert to A, it will have two possibilities. It follows that it is impossible to refer to the non-static data members of the direct A object given a Derived object.

Notice that accessibility is orthogonal to visibility. Something can be accessible even tho it's not visible (for example by refering to it by a qualified name), and something can be visible even though it's not accessible. Even if all the above derivations would be declared private, the problem would still show up: Access is checked last - it won't influence name lookup or conversion rules.

Also, anyone can cast to an unambiguous private base class with defined behavior (the C++ Standard makes an exception for this) using a C-style cast, even if normally access wouldn't be granted to do so. And then there are still friends and the class itself that could freely convert.


Johannes' answer covers the basic facts. But there's a little more to it. So, consider

struct Base
{
    Base( int ) {}
    void foo() const {}
};

struct Intermediate: Base
{
    Intermediate( int x )
        : Base( x )
    {}
};

struct Derived: Intermediate, Base
{
    Derived( int x )
        : Intermediate( x )
        , Base( x )         // OK
    {}
};

int main()
{
    Derived o( 667 );
    o.foo();                // !Oops, ambiguous.
    o.Base::foo();          // !Oops, still ambiguous.
}

When I compile I get, as by now (after Johannes' answer) you'll expect,

C:\test> gnuc x.cpp
x.cpp:15: warning: direct base 'Base' inaccessible in 'Derived' due to ambiguity
x.cpp: In function 'int main()':
x.cpp:25: error: request for member 'foo' is ambiguous
x.cpp:4: error: candidates are: void Base::foo() const
x.cpp:4: error:                 void Base::foo() const
x.cpp:26: error: 'Base' is an ambiguous base of 'Derived'

C:\test> msvc x.cpp
x.cpp
x.cpp(15) : warning C4584: 'Derived' : base-class 'Base' is already a base-class of 'Intermediate'
        x.cpp(2) : see declaration of 'Base'
        x.cpp(7) : see declaration of 'Intermediate'
x.cpp(25) : error C2385: ambiguous access of 'foo'
        could be the 'foo' in base 'Base'
        or could be the 'foo' in base 'Base'
x.cpp(25) : error C3861: 'foo': identifier not found

C:\test> _

How to resolve depends on whether it's all right with a single sub-object of class Base (as is the case when Base is a pure interface), or Intermediate really requires its own Base sub-object.

The latter case, two Base sub-objects, is probably not what you want, but if you want that then then one cure is to introduce yet another intermediate class, say, ResolvableBase.

Like:

struct Base
{
    Base( int ) {}
    void foo() const {}
};

struct Intermediate: Base
{
    Intermediate( int x )
        : Base( x )
    {}
};

struct ResolvableBase: Base
{
    ResolvableBase( int x ): Base( x ) {}
};

struct Derived: Intermediate, ResolvableBase
{
    Derived( int x )
        : Intermediate( x )
        , ResolvableBase( x )
    {}
};

int main()
{
    Derived o( 667 );
    o.ResolvableBase::foo();    // OK.
}

In the first case, where e.g. Base is an interface and only one Base sub-object is needed, you can use virtual inheritance.

Virtual inheritance generally adds some runtime overhead, and Visual C++ is not too fond of it.

But it lets you "inherit in" an implementation of an interface, like in Java and C#:

struct Base
{
    Base( int ) {}
    virtual void foo() const = 0;
};

struct Intermediate: virtual Base
{
    Intermediate( int x )
        : Base( x )
    {}
    void foo() const {}     // An implementation of Base::foo
};

struct Derived: virtual Base, Intermediate
{
    Derived( int x )
        : Base( x )
        , Intermediate( x )
    {}
};

int main()
{
    Derived o( 667 );
    o.foo();    // OK.
}

Subtlety: I changed the inheritance list order in order to avoid g++ sillywarnings about initialization order.

Annoyance: Visual C++ issues sillywarning C4250 about inheritance (of implementation) via dominance. It's like "warning: you're using a standard main function". Oh well, just turn it off.

Cheers & hth.,