As a theoretical question: Why do you think they (the C++ standard
committee) made the decision (in this inheritance situation) to hide
functions by name rather than "normally" by name and parameters (it
seems a bit unlogical) ?
Doing otherwise would have been inconsistent with the rest of the name
lookup rules. Just for example:
char x;
void f() {
int x;
x = 'a';
}
At least in C and C++, the fact that we're assigning a char to x
doesn't mean that the assignment is really to the global x. The x that
is visible inside the function is the one that's defined inside of the
function, and the type we're assigning doesn't change that.
If this was changed, it would also cause a serious difference between C
and C++ in a different way: in C, a character literal has type int,
where in C++ it has type char. As such, if the name lookup was done
this way, compiling this as C would assign to the local variable, but
compiling it as C++ would assign to the global.
Changing this rule also makes lot of code relatively fragile. For
example:
class X {
int f(int);
int g() { f('a'); }
}
I _know_ that X::g() calls X::f(), implicitly casting the char to an
int before doing so. Some would argue that this implicit conversion is
undesirable, but at least I know what's going to happen -- and since
it's widening a char to an int, the conversion is fairly safe.
Consider what happens if the function signature worked across block
scopes though: if there was a global function f(char), it would get
called in preference to X::f().
While these problems may be less obvious when inheritance is involved,
I think there would be serious problems there as well. Consider, for
example:
class base {
void utility(char);
};
class derived : public base {
void utility(int);
void g() {
utility('a');
};
Now, in this case it seems likely that the programmer _intended_ to
call derived::utility -- in fact, he might not be consciously aware
that the base class even contains a function by that name (since it's
private, he shouldn't have to be). Unfortunately, he's passing a char
instead of an int, so if base::utility was visible it would be the
better match (and since it's not accessible, the code wouldn't
compile).
In this particular case, that could be "cured" by changing that rule as
well, so that 'private' functions would be invisible rather than
inaccessible in a derived class.
Knowing how things go, however, there are almost certainly a few other
things that depend on that rule being the way it is -- and each of them
undoubtedly has a few more dependencies, and so on. You could
undoubtedly create a coherent language with these things changed, but
the result wouldn't be a C++ that was marginally different in one
particular area, but a language that was considerably different
throughout.
Compiling this language would be considerably more difficult -- in
fact, compiling ordinary code would become somewhat similar to
compiling an exported template with the current language, and for much
the same reason: the meaning of a particular name wouldn't be defined
entirely by the code itself, but by the entire context within which the
code was used. Likewise, many of the surprising things that can arise
with two-phase name lookup in an exported template could then affect
non-template code as well. Rather than being able to read and
understand code in small, digestible chunks, you'd have to read and
(sort of) undertand all the context before any one piece could be
understood.