从你问题的措辞来看(你用了“隐藏”这个词),你已经知道这里发生了什么。这种现象被称为“名字隐藏”。出于某种原因,每当有人问为什么会发生名称隐藏的问题时,回答的人要么说这叫“名称隐藏”,并解释它是如何工作的(你可能已经知道了),要么解释如何覆盖它(你从来没有问过),但似乎没有人关心解决真正的“为什么”问题。
The decision, the rationale behind the name hiding, i.e. why it actually was designed into C++, is to avoid certain counter-intuitive, unforeseen and potentially dangerous behavior that might take place if the inherited set of overloaded functions were allowed to mix with the current set of overloads in the given class. You probably know that in C++ overload resolution works by choosing the best function from the set of candidates. This is done by matching the types of arguments to the types of parameters. The matching rules could be complicated at times, and often lead to results that might be perceived as illogical by an unprepared user. Adding new functions to a set of previously existing ones might result in a rather drastic shift in overload resolution results.
For example, let's say the base class B has a member function foo that takes a parameter of type void *, and all calls to foo(NULL) are resolved to B::foo(void *). Let's say there's no name hiding and this B::foo(void *) is visible in many different classes descending from B. However, let's say in some [indirect, remote] descendant D of class B a function foo(int) is defined. Now, without name hiding D has both foo(void *) and foo(int) visible and participating in overload resolution. Which function will the calls to foo(NULL) resolve to, if made through an object of type D? They will resolve to D::foo(int), since int is a better match for integral zero (i.e. NULL) than any pointer type. So, throughout the hierarchy calls to foo(NULL) resolve to one function, while in D (and under) they suddenly resolve to another.
另一个例子是在《c++的设计与发展》第77页:
class Base {
int x;
public:
virtual void copy(Base* p) { x = p-> x; }
};
class Derived : public Base{
int xx;
public:
virtual void copy(Derived* p) { xx = p->xx; Base::copy(p); }
};
void f(Base a, Derived b)
{
a.copy(&b); // ok: copy Base part of b
b.copy(&a); // error: copy(Base*) is hidden by copy(Derived*)
}
如果没有这个规则,b的状态将被部分更新,从而导致切片。
在设计语言时,这种行为被认为是不可取的。作为一种更好的方法,决定遵循“名称隐藏”规范,这意味着每个类对于它声明的每个方法名都以“干净的表”开始。为了重写此行为,需要用户执行一个显式操作:最初是继承方法的重新声明(目前已弃用),现在显式使用using-declaration。
正如你在最初的帖子中正确地观察到的(我指的是“非多态”注释),这种行为可能被视为违反了类之间的IS-A关系。这是事实,但显然当时的人们认为,最终隐藏名字会被证明是一个较轻的邪恶。