I accidentally run into the problem having member variables with the same name in classes used in multiple inheritance. My basic idea was that the member variables are simple "merged", i.e. a multiple declaration happens. The compiler did not tell me even a warning, see the MWE below. I understand that it is a bad idea to have variables with the same name, so I think it is at least ambiguous to refer to them in the way I do; so I expected at least a warning or maybe an error.
1) Why the compiler does not write out at least a warning?
2) How handling of those variables is solved internally? (I guess aliases like HW::I and Other::I are used, but how they relate to SW1::I and SW2::I?)
#include <iostream>
struct Other { int I;};
struct HW { int I;};
struct SW1 : Other, HW { int I;};
struct SW2 : HW, Other { int I;};
struct D : SW1 { };
struct E : SW2 { };
int main()
{
E* e = new E;
D* d = new D;
e->I = 3;
SW1* pc1 = dynamic_cast<SW1*>(d);
pc1->I = 2;
std::cerr << d->I;
std::cerr << e->I;
SW2* pc2 = dynamic_cast<SW2*>(e);
pc2->I = 1;
std::cerr << d->I;
std::cerr << e->I;
}
The compiler is correct not to diagnose any problems with your code. The code, as you've constructed it is not ambiguous. Essentially, a name is ambiguous if it is an equally good match for more than one variable (or class member in your case).
When evaluating e->I
, the first candidate found is the I
that is a member (via inheritance) of class SW2
. The members of I
that SW2
inherits from its base classes are not as good a match as the member defined directly by Sw2
.
Similarly, pc1->I
is unambiguously the member of SW1
, d->I
is the same, and pc2->I
is unambiguously the member of the base class SW2
.
Ambiguity would occur in evaluating e->I
if SW2
did not have its own member named I
(i.e. struct SW2: HW, Other {};
( . In that case, when evaluating e->I
, the name resolution looks in SW2
for a member named I
, and doesn't find it. Resolving the name then considers the two base classes, HW
and Other
, which both have a member named I
. They are equally good matches, so the expression e->I
is ambiguous - and the compiler will issue a diagnostic i.e. an error (not just a warning). In that case, it is possible for the programmer to explicitly resolve the ambiguity using the scope (::
) operator. For example, e->HW::I
or e->Other::I
which fully qualifies the name.
You are right that such multiple usage of a name within a class hierarchy is a bad idea. Both because it can be difficult to correctly resolve the ambiguity in a way that makes sense to a compiler, and because mere mortals will often have trouble following the logic.