C++ 为什么派生类中的重写函数隐藏了基类的其他重载?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1628768/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why does an overridden function in the derived class hide other overloads of the base class?
提问by Aman Aggarwal
Consider the code :
考虑代码:
#include <stdio.h>
class Base {
public:
virtual void gogo(int a){
printf(" Base :: gogo (int) \n");
};
virtual void gogo(int* a){
printf(" Base :: gogo (int*) \n");
};
};
class Derived : public Base{
public:
virtual void gogo(int* a){
printf(" Derived :: gogo (int*) \n");
};
};
int main(){
Derived obj;
obj.gogo(7);
}
Got this error :
得到这个错误:
>g++ -pedantic -Os test.cpp -o test test.cpp: In function `int main()': test.cpp:31: error: no matching function for call to `Derived::gogo(int)' test.cpp:21: note: candidates are: virtual void Derived::gogo(int*) test.cpp:33:2: warning: no newline at end of file >Exit code: 1
Here, the Derived class's function is eclipsing all functions of same name (not signature) in the base class. Somehow, this behaviour of C++ does not look OK. Not polymorphic.
在这里,派生类的函数覆盖了基类中所有同名(不是签名)的函数。不知何故,C++ 的这种行为看起来不太好。不是多态的。
回答by AnT
Judging by the wording of your question (you used the word "hide"), you already know what is going on here. The phenomenon is called "name hiding". For some reason, every time someone asks a question about whyname hiding happens, people who respond either say that this called "name hiding" and explain how it works (which you probably already know), or explain how to override it (which you never asked about), but nobody seems to care to address the actual "why" question.
根据您问题的措辞(您使用了“隐藏”一词),您已经知道这里发生了什么。这种现象被称为“姓名隐藏”。出于某种原因,每次有人问为什么会发生名称隐藏的问题时,回答的人要么说这称为“名称隐藏”并解释它的工作原理(您可能已经知道),或者解释如何覆盖它(您可能已经知道)从来没有问过),但似乎没有人关心解决实际的“为什么”问题。
The decision, the rationale behind the name hiding, i.e. whyit actually was designed into C++, is to avoid certain counterintuitive, 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.
决定,即隐藏名称背后的基本原理,即为什么它实际上被设计到 C++ 中,是为了避免某些违反直觉、不可预见和潜在危险的行为,如果允许继承的重载函数集与当前的重载函数集混合,则可能发生这些行为。给定类中的重载。您可能知道在 C++ 中,重载解析的工作原理是从候选集中选择最佳函数。这是通过将参数类型与参数类型匹配来完成的。匹配规则有时可能很复杂,并且经常导致没有准备的用户可能认为不合逻辑的结果。向一组先前存在的函数添加新函数可能会导致重载解析结果发生相当大的变化。
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.
例如,假设基类B
有一个成员函数foo
,它接受一个类型为 的参数void *
,并且所有对 的调用foo(NULL)
都解析为B::foo(void *)
。假设没有隐藏名称,这B::foo(void *)
在从B
. 但是,假设在类的某个 [间接,远程] 后代D
中定义B
了一个函数foo(int)
。现在,没有名字隐藏D
同时具有foo(void *)
与foo(int)
可见光和参与重载决议。foo(NULL)
如果通过类型为 的对象,调用将解析为哪个函数D
?它们将解析为D::foo(int)
,因为它int
是整数零的更好匹配(即NULL
) 比任何指针类型。因此,在整个层次结构中调用foo(NULL)
解析为一个函数,而在D
(和下面)它们突然解析为另一个函数。
Another example is given in The Design and Evolution of C++, page 77:
The Design and Evolution of C++, page 77 中给出了另一个例子:
class Base {
int x;
public:
virtual void copy(Base* p) { x = p-> x; }
};
class Derived{
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*)
}
Without this rule, b's state would be partially updated, leading to slicing.
如果没有这条规则,b 的状态将被部分更新,导致切片。
This behavior was deemed undesirable when the language was designed. As a better approach, it was decided to follow the "name hiding" specification, meaning that each class starts with a "clean sheet" with respect to each method name it declares. In order to override this behavior, an explicit action is required from the user: originally a redeclaration of inherited method(s) (currently deprecated), now an explicit use of using-declaration.
在设计语言时,这种行为被认为是不受欢迎的。作为一种更好的方法,决定遵循“名称隐藏”规范,这意味着每个类都以其声明的每个方法名称的“空白表”开头。为了覆盖此行为,需要用户进行显式操作:最初是对继承方法的重新声明(目前已弃用),现在显式使用 using 声明。
As you correctly observed in your original post (I'm referring to the "Not polymorphic" remark), this behavior might be seen as a violation of IS-A relationsip between the classes. This is true, but apparently back then it was decided that in the end name hiding would prove to be a lesser evil.
正如您在原始帖子中正确观察到的(我指的是“非多态”评论),这种行为可能被视为违反了类之间的 IS-A 关系。这是真的,但显然当时决定隐藏姓名最终会证明是一种较小的邪恶。
回答by Drew Hall
The name resolution rules say that name lookup stops in the first scope in which a matching name is found. At that point, the overload resolution rules kick in to find the best match of available functions.
名称解析规则说名称查找在找到匹配名称的第一个范围内停止。那时,重载决议规则开始寻找可用函数的最佳匹配。
In this case, gogo(int*)
is found (alone) in the Derived class scope, and as there's no standard conversion from int to int*, the lookup fails.
在这种情况下,gogo(int*)
在派生类范围中找到(单独),并且由于没有从 int 到 int* 的标准转换,查找失败。
The solution is to bring the Base declarations in via a using declaration in the Derived class:
解决方案是通过派生类中的 using 声明引入 Base 声明:
using Base::gogo;
...would allow the name lookup rules to find all candidates and thus the overload resolution would proceed as you expected.
...将允许名称查找规则找到所有候选对象,因此重载解析将按您的预期进行。
回答by JaredPar
This is "By Design". In C++ overload resolution for this type of method works like the following.
这是“按设计”。在 C++ 中,这种方法的重载解析的工作方式如下。
- Starting at the type of the reference and then going to the base type, find the first type which has a method named "gogo"
- Considering only methods named "gogo" on that type find a matching overload
- 从引用的类型开始,然后转到基类型,找到第一个具有名为“gogo”的方法的类型
- 仅考虑该类型上名为“gogo”的方法才能找到匹配的重载
Since Derived does not have a matching function named "gogo", overload resolution fails.
由于 Derived 没有名为“gogo”的匹配函数,因此重载解析失败。
回答by Sandeep Singh
Name hiding makes sense because it prevents ambiguities in name resolution.
名称隐藏很有意义,因为它可以防止名称解析中的歧义。
Consider this code:
考虑这个代码:
class Base
{
public:
void func (float x) { ... }
}
class Derived: public Base
{
public:
void func (double x) { ... }
}
Derived dobj;
If Base::func(float)
was not hidden by Derived::func(double)
in Derived, we would call the base class function when calling dobj.func(0.f)
, even though a float can be promoted to a double.
如果在 DerivedBase::func(float)
中没有隐藏Derived::func(double)
,我们会在调用时调用基类函数dobj.func(0.f)
,即使浮点数可以提升为双精度数。
Reference: http://bastian.rieck.ru/blog/posts/2016/name_hiding_cxx/
参考:http: //bastian.rieck.ru/blog/posts/2016/name_hiding_cxx/