首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >隐藏C++成员函数的原因

隐藏C++成员函数的原因
EN

Stack Overflow用户
提问于 2012-08-13 00:39:35
回答 3查看 13.6K关注 0票数 27

可能重复:

name hiding and fragile base problem

我熟悉涉及成员函数隐藏的规则。基本上,具有与基类函数同名的函数的派生类实际上并不会重载基类函数-它会完全隐藏基类函数。

代码语言:javascript
复制
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!
}

因此,您可以通过using声明来解决此问题。但我的问题是,基类函数隐藏的原因是什么?这是标准委员会的一个“特性”还是一个“错误”?当编译器找不到d.foo(123)的匹配项时,为什么编译器不能在基类中查找匹配的重载,这是不是有一些技术原因

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-08-13 01:46:22

名称查找的工作方式是在当前作用域中查找匹配的名称,如果什么都没有找到,那么它会在封闭范围内查找,如果什么都没有找到,它就会在封闭范围内查找,以此类推,直到到达全局命名空间。

这并不是特定于类的,在这里你会得到完全相同的名字:

代码语言:javascript
复制
#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();
}

尽管outer::foo(char)更适合于调用,但foo('c')名称查找在找到outer::inner::foo(int)后停止(即outer::foo(char)是隐藏的),因此程序会打印inner

如果没有隐藏成员函数名,这意味着类作用域中的名称查找与非类作用域中的名称查找的行为不同,这将是不一致和混乱的,并使C++更难学习。

因此,没有什么技术原因不能更改名称查找规则,但对于成员函数和其他类型的名称查找,它们必须更改,这会使编译器变慢,因为即使在当前作用域中找到匹配的名称,编译器也必须继续搜索名称。明智的是,如果在当前作用域中有一个名称,那么它可能就是您想要的名称。作用域A中的调用可能想要查找该作用域中的名称,例如,如果两个函数在同一名称空间中,它们可能是相关的(属于同一模块或库),因此如果一个函数使用另一个函数的名称,则可能意味着调用同一作用域中的另一个函数。如果这不是您想要的,那么使用显式限定或using声明来告诉编译器其他名称应该在该作用域中可见。

票数 23
EN

Stack Overflow用户

发布于 2012-08-13 00:54:17

这是标准委员会的一个“特性”还是一个“错误”?

这绝对不是一个错误,因为它在标准中有明确的规定。这是一个特性。

当编译器找不到d.foo(123)的匹配项时,为什么编译器不能在基类中查找匹配的重载,这是不是有什么技术原因?

从技术上讲,编译器可以在基类中查找。从技术上讲,。但如果它这样做了,它就会违反标准设定的规则。

但我的问题是,基类函数隐藏的原因是什么?

除非委员会有人给出答案,否则我想我们只能猜测。基本上,有两种选择:

  • 如果我在派生类中声明了一个同名函数,则可以通过派生class
  • don't

直接访问基类的同名函数

它可以通过抛硬币来确定(...ok,也许不是)。

一般来说,想要一个与基类同名的函数的原因是什么?有不同的功能--你更有可能使用多态性。对于处理不同的情况(不同的参数),如果这些情况不存在于基类中,则策略模式可能更适合处理工作。所以当你真的想隐藏函数时,函数隐藏很可能会起作用。您对基类实现不满意,所以您提供了自己的基类实现,并提供了使用using的选项,但只在您想要的时候使用。

我认为这只是一种机制,让你在拥有具有相同名称和不同签名的函数之前三思而后行。

票数 10
EN

Stack Overflow用户

发布于 2012-08-13 01:35:48

我相信@Lol4t0是非常正确的,但我会更有力地陈述事情。如果你允许这样做,你最终会有两种可能:要么在几乎整个语言中做很多其他的改变,要么你最终会得到一些几乎完全崩溃的东西。

为了实现这一点,您所做的其他更改将是完全重写重载的方式--您必须至少更改所采取步骤的顺序,可能还需要更改步骤本身的细节。现在,编译器查找名称,然后形成一个重载集,解析重载,然后检查对所选重载的访问。

为了让它更好地工作,你必须改变它,首先检查访问,并且只向重载集中添加可访问的函数。这样,至少@Lol4t0答案中的示例可以继续编译,因为Base::foo永远不会被添加到重载集中。

然而,这仍然意味着添加到基类的接口可能会导致严重的问题。如果Base最初不包含foo,并且添加了一个公共foo,那么main中对d.foo()的调用将突然做一些完全不同的事情,并且(再次)它将完全不受编写Derived的人的控制。

为了解决这个问题,你只需要对规则做一个基本的改变:禁止函数参数的隐式转换。此外,您可以更改重载解析,以便在平局的情况下,函数的派生最多/最局部的版本优先于派生/外部较少的作用域。有了这些规则,对d.foo(5.0)的调用从一开始就不能解析为Derived::foo(int)

然而,这只会留下两种可能性:要么对自由函数的调用与对成员函数的调用具有不同的规则(只允许对自由函数进行隐式转换),要么完全放弃与C的所有兼容性(即,还禁止在所有函数参数中进行隐式转换,这将破坏大量现有代码)。

总而言之:为了在不完全破坏语言的情况下改变这一点,你还必须做很多其他的改变。几乎可以肯定地说,创建一种以这种方式工作的语言是可能的,但是当您完成工作时,它将不是只做了一点小改动的C++ --它将是一种完全不同的语言,与C++或C或许多其他语言不太一样。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11923890

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档