ATTENTION: ANSWER FUNDAMENTALLY EDITED !
Analysis of the problem:
You are using multiple inheritance with a diamond problem.
More specifically, your structure D
inherits the same base A
class three times: once directly (struct D: C,A
) and twice indirectly (via inheritance of C). As the base classes are not virtual, there are 3 different A sub-objects for D. C++11 standard section 10.1/4-5 calls this a lattice:
Normally, you would then disambiguate members of each A
with explicit qualification telling the compiler which of the 3 A
subobject you are refering to. This is explained in C++11 section 10.1/5. The syntax for members should be A::a
, C::a
and B::a
within the scope of D
, each eventually preceded by D::
if you are outside.
Unfortunately, the member name lookup logic in C++11 section 10.2/5-6 ensures that the direct A
base will allways make the other indirect A
bases ambiguous, despite explicit qualification (or even using
statements).
Definitive solution:
As the problem is caused by the direct base class, and the fact that there are no ways to diambiguate this one from the others, the only really working solution is to use an empty intermediary class to force a different name:
struct Ob{ int v; }; // v aded here to allow verification of copy of all members
struct A { Ob a; };
struct B : A { Ob b; };
struct A1 : A {}; // intermediary class just for diambiguation of A in C
struct C : A1, B { Ob c; }; // use A1 instead of A
struct A2 : A { }; // intermediary class just for diambiguation of A in D
struct D : C, A2 { // use A2 instead of A
Ob d;
D() { }
D(const D& _d) : C(_d), A2(_d), d(_d.d) { }
};
int main(int ac, char**av)
{
cout << "Multiple inheritance\n";
D x;
x.A2::a.v = 1; // without A2:: it's ambiguous
x.A1::a.v = 2; // without A1:: it's ambiguous
x.B::a.v = 3;
x.b.v = 4;
x.d.v = 5;
D y = x;
cout << "The moment of truth: if not 1 2 3 4 5, there's a problem!\n";
cout << y.A2::a.v << endl;
cout << y.A1::a.v << endl;
cout << y.B::a.v << endl;
cout << y.b.v << endl;
cout << y.d.v << endl;
}
This code compiles and work with MSVC2013, clang 3.4.1, and gcc 4.9.
Other (non-)solutions:
My previous answer was based only on explicit qualification. Despite many criticism, I really got it compiled and tested successfully on MSVC2013 ! However their was a weird thing: in the editor intelisence highlighted an ambiguity but the compilation ran fine without any error. I first thought that this was an intelisence bug, but now realize that this was a compiler non compliance (bug ?)
The answer suggesting D(const D& other) : C(other), A((const B)other), d(other.d)
compiles, but does not pass the test. Why ? because A((const B)other)
will understand other
as being a B
. So the A
directly in D
would get initialized with the value of the A
indirectly inherited from B
(so another A
). This is an extremely nasty error and it took me a while to notice.
Of course you could use virtual base classes. Then there will be only a single A
subobject in D, which solves many problems. However I don't konw what you are designing, and some designs require a lattice rather than a virtualized diamond.
If you could afford a two steps copy (step 1: default initialisation of the base; step 2: copying the target value on the base), there are certainly approaches using diambiguated member functions returning the reference of the correct base. But that might be more tricky and error prone than the simple solution presented above.
D::method() { a = A(); }
orD d = D(); d.a = A();
. – MhdD::A
refers to a type, not a subobject. And the problem never was naming the base class, it's the argument. When you pass_d
toA
(orD::A
), it needs to be implicitly converted to the argument type,const A&
. But the implicit conversion fails due to ambiguity. There is no way to name theA
subobject of aD
which was introduced atD
(rather than part of theC
base subobject) – BearishD::A
refers to a type, which happens to to be a class of D which makes this a valid syntax according to C++11 sect.12.6.2. For member disambiguation, see sect.10.1/5 – Fulfil