问题
Spoiler alert: Maybe a stupid question. :)
#include <iostream>
using namespace std;
class Base
{
public:
virtual void YourMethod(int) const = 0;
};
class Intermediate : private Base
{
public:
virtual void YourMethod(int i) const
{
cout << \"Calling from Intermediate\" << i << \"\\n\";
}
};
class Derived : private Intermediate, public Base
{
public:
void YourMethod(int i) const
{
cout << \"Calling from Derived : \" << i << \"\\n\";
}
};
int main()
{
}
Can someone Explain to me why this throws the compiler warning:
main.cpp:21: warning: direct base ‘Base’ inaccessible in ‘Derived’ due to ambiguity
Now, I understand that there is no way this code will work. I want to know why. Base is private to Intermediate so it should not be visible to Derived through Intermediate. So where does the ambiguity come from? In constructor?
回答1:
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.
回答2:
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.,
来源:https://stackoverflow.com/questions/4118412/inaccessible-direct-base-caused-by-multiple-inheritance