可能重复:
名称隐藏和脆弱的基本问题
我熟悉涉及成员函数隐藏的规则.基本上,具有与基类函数同名的函数的派生类实际上不会重载基类函数 - 它完全隐藏它.
struct Base
{
void foo(int x) const
{
}
};
struct Derived : public Base
{
void foo(const std::string& s) { }
};
int main()
{
Derived d;
d.foo("abc");
d.foo(123); // Will not compile! Base::foo is hidden!
}
Run Code Online (Sandbox Code Playgroud)
所以,您可以通过using声明解决这个问题.但我的问题是,基类函数隐藏的原因是什么?这是标准委员会的"特征"还是"错误"?是否存在一些技术原因导致编译器在找不到匹配项时无法在Base类中查找匹配重载d.foo(123)?
Jon*_*ely 18
名称查找的工作原理是查看匹配名称的当前范围,如果找不到任何内容,则查找封闭范围,如果找不到任何内容,则查看封闭范围等,直到到达全局名称空间.
这不是特定于类,您在此处隐藏的名称完全相同:
#include <iostream>
namespace outer
{
void foo(char c) { std::cout << "outer\n"; }
namespace inner
{
void foo(int i) { std::cout << "inner\n"; }
void bar() { foo('c'); }
}
}
int main()
{
outer::inner::bar();
}
Run Code Online (Sandbox Code Playgroud)
虽然outer::foo(char)是一个更好的匹配呼叫foo('c')名称查找停止后发现outer::inner::foo(int)(即outer::foo(char)隐藏),所以程序打印inner.
如果没有隐藏成员函数名,这意味着类范围中的名称查找与非类范围的行为不同,这将是不一致和混乱的,并使C++更难学习.
所以没有技术上的原因,名称查找规则无法更改,但是它们必须针对成员函数和其他类型的名称查找进行更改,这会使编译器变慢,因为他们必须继续搜索名称,即使之后在当前范围内查找匹配的名称.明智地,如果当前范围内有名称,那么可能就是您想要的名称.范围内的调用A可能想要在该范围内查找名称,例如,如果两个函数位于相同的命名空间中,它们可能是相关的(同一模块或库的一部分),因此如果使用另一个的名称,则可能意味着调用同一范围内的那个.如果那不是您想要的,那么使用显式限定或使用声明来告诉编译器在该范围内应该可以看到其他名称.
Luc*_*ore 10
这是标准委员会的"特征"还是"错误"?
这绝对不是一个错误,因为它在标准中明确规定.这是一个功能.
是否存在一些技术原因导致编译器在找不到d.foo(123)的匹配项时无法在Base类中查找匹配重载?
从技术上讲,编译器可以查看基类.技术上.但如果确实如此,它将违反标准规定的规则.
但我的问题是,基类函数隐藏的原因是什么?
除非委员会有人给出答案,否则我认为我们只能推测.基本上,有两种选择:
它可以通过翻转硬币来确定(......好吧,也许不是).
一般来说,想要一个与基类名称相同的函数的原因是什么?有不同的功能 - 你更可能使用多态.对于处理不同情况(不同参数),如果基类中不存在这些情况,策略模式可能更适合处理作业.因此,当您确实想要隐藏函数时,最有可能的函数隐藏会生效.您对基类实现不满意,因此您可以使用自己的选项using,但只能在您需要时使用.
我认为这只是一种让你在拥有相同名称和不同签名的功能之前三思而后行的机制.
我相信@Lol4t0 是非常正确的,但我会更强烈地陈述事情。如果你允许这样做,你最终会遇到两种可能性:要么在几乎整个语言中进行大量其他更改,要么你最终会得到几乎完全损坏的东西。
为了让它发挥作用,您要做的其他更改是彻底修改重载的完成方式——您必须至少更改所采取步骤的顺序,并且可能还需要更改步骤本身的详细信息。现在,编译器查找名称,然后形成重载集,解析重载,然后检查对所选重载的访问。
为了使这项工作顺利进行,您几乎必须将其更改为首先检查访问权限,并且仅将可访问的函数添加到重载集中。这样,至少 @Lol4t0 答案中的示例可以继续编译,因为Base::foo永远不会添加到重载集中。
然而,这仍然意味着添加到基类的接口可能会导致严重的问题。如果Base最初不包含foo,并且添加了public ,那么对to 的foo调用会突然做一些完全不同的事情,并且(再次)它将完全超出编写者的控制范围。maind.foo()Derived
为了解决这个问题,您必须对规则进行相当根本的更改:禁止函数参数的隐式转换。除此之外,您还可以更改重载解析,以便在出现平局的情况下,函数的最派生/最本地版本比派生程度较低/外部作用域更受青睐。根据这些规则,对 的调用从一开始就不可能d.foo(5.0)解决。Derived::foo(int)
然而,这只留下两种可能性:要么对自由函数的调用与对成员函数的调用具有不同的规则(仅允许自由函数进行隐式转换),要么完全放弃与 C 的所有兼容性(即也禁止隐式转换)在所有函数参数中,这会破坏大量现有代码)。
总结一下:要在不完全破坏语言的情况下更改此设置,您还必须进行相当多的其他更改。几乎可以肯定,创建一种以这种方式工作的语言是可能的,但是当你完成时,它不会是 C++,只需进行一点小小的改变——它将是一种完全不同的语言,与 C++ 或 C 不太相似。 ,或其他很多东西。
| 归档时间: |
|
| 查看次数: |
10327 次 |
| 最近记录: |