9

考虑:

#include <iostream>

using namespace std;

struct A {
  virtual void f() { cout << "A::f" << endl; }
  virtual void f() const { cout << "A::f const" << endl; }
};

struct B : public A {};

struct C : public A {
   virtual void f() { cout << "C::f" << endl; }
};


int main()
{
   const B b;
   b.f();   // prints "A::f const"

   const C c;
   c.f();
   // Compile-time error: passing ‘const C’ as ‘this’ argument of
   //   ‘virtual void C::f()’ discards qualifiers
}

(我正在使用 GCC。)

所以 f() 的 const 版本似乎隐藏在 C 中。这对我来说很有意义,但它是标准规定的吗?

4

4 回答 4

6

我将(再一次)链接这篇很棒的文章

首先,[编译器]查看直接作用域,在本例中是类 C 的作用域,并列出它可以找到的所有名为 f 的函数(无论它们是否可访问,甚至是否采用正确数量的参数)。只有当它不这样做时,它才会继续“向外”进入下一个封闭范围[...]

所以是的,const版本f是隐藏的,这是完全正常的。正如 Simone 所指出的,您可以使用using语句来引入A::f范围C

于 2010-11-11T09:30:37.757 回答
3

是的。你可以写:

struct C : public A {
   virtual void f() { cout << "C::f" << endl; }
   using A::f;       
};

使您的代码编译:

int main()
{
   const B b;
   b.f();   // prints "A::f const"

   const C c;
   c.f();   // prints "A::f const"
}

有关更多信息,您可以参考 2010 C++ 草案文档(您可以在此处找到)第 10.2.(3-4) 章。

于 2010-11-11T09:21:22.917 回答
3

隐藏基本成员的不是虚拟性或 const-ness(或缺乏),任何派生方法都隐藏了同名的基本方法。这样做是为了改善脆弱的基类问题。

想象一下您的代码正在运行(可能多年),如下所示,删除了不相关的部分:

struct Base {
};

struct Derived : Base {
  void f(double);
}

void g(Derived &d) {
  d.f(42);
}

然后你需要修改 Base 以包含一个做一些完全不同的事情的方法,但是,出于某种原因,你想将它命名为“f”:

struct Base {
  void f(int);
};

如果没有此规则,则需要手动评估 Derived 调用 f 的每次使用 - 如果 Base 在提供给其他人的库中,您甚至可能无法访问这些其他用途!面对用户定义的(隐式)转换,情况会变得更糟。

相反,决定要求派生类明确声明他们希望使用 using 声明从 Base 导入给定名称。这条规则可能令人惊讶,我不确定它是否对今天的语言有净好处,但他们没有问我——当时,我可能只能用两个音节的词来回答他们。:)

于 2010-11-11T09:37:43.350 回答
2

插入using B::f;

struct C : public A { 
   using A::f;
   virtual void f() { cout << "C::f" << endl; } 
}; 

C++ 标准 2003。13.2 p.1:

如果两个同名的函数声明在同一范围内并且具有等效的参数声明(13.1),则它们引用同一个函数。派生类的函数成员与基类中的同名函数成员不在同一范围内。

从而C::f隐藏一切A::f

于 2010-11-11T09:20:42.930 回答