Re: Why is this ambiguous
Alf P. Steinbach wrote:
But why is it like that?
It doesn't seem very practical or intuitive; on the surface it seems
like an arbitrary special case restriction, instead of generalization?
Well, what seems "intuitive" is subjective, and opinions will differ.
I can try to explain why it makes sense to me.
Prior to the introduction of multiple inheritance, scopes in C++ always
formed a strict hierarchy...every scope was enclosed by exactly one
scope (except global scope, of course), and names in the enclosed scope
always hid names from enclosing scopes. So overload resolution never
had to cope with two names from different scopes, since no colliding
names from different scopes were ever visible.
Multiple inheritance introduced a strange and unfamiliar structure:
peer enclosing scopes....that is, multiple scopes (base class scopes)
which all enclose another scope (the derived class scope) but which do
not enclose each other. Put another way, the scope of a derived class
can be enclosed by two scopes which are themselves unrelated. So the
question of arbitration arises: if there's a name conflict between
peer scopes, which name, if any, gets hidden?
There are basically three alternative models for how you handle names
from peer enclosing scopes within their "jointly enclosed" scope:
(1) Impose an ordering: we could say that, given
class derived: base1, base2 {};
that names from base2 hide names from base1 (or vice versa), so that in
the case of a conflict, only the name from one base class is in the
scope of the derived class.
(2) Impose merging: automatically redeclare all names from the base
classes in the scope of the derived class. This is more or less the
behavior that some seem to expect, and it leads to questions like the
OP's. I believe this alternative was rejected as error prone: adding
a base class could silently, and surprisingly, change the behavior of
existing member functions in the derived class, if there happened to be
a name collision, and if the new class's method happened to be a better
match than the one previously called.
(3) Ask the programmer what to do: declare that name collisions
between base class members are ambiguous, and let the programmer
explicitly tell the compiler what is intended. A minor inconvenience,
but avoids surprises. (Doesn't C++ have enough surprises?) More
importantly, it avoids *silent* changes which might surprise the
programmer.
Now, arguably, the balance of arguments between options 2 and 3 may
have shifted, now that we have introduced templates and metaprogramming
techniques that involve inheriting from many, many base classes for the
express purpose of composing scopes. (And we've introduced ADL, which
is also an exception to the
"one-scope-to-an-overload-resolution-customer" rule.) But for the
moment, that's where we are.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]