【问题标题】:Does overriding a non-const virtual method hide a const overload?覆盖非常量虚拟方法是否隐藏了 const 重载?
【发布时间】:2011-05-08 08:34:43
【问题描述】:

考虑:

#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 中。这对我来说很有意义,但它是标准规定的吗?

【问题讨论】:

  • “虚拟”是一条红鲱鱼。我们在这里没有虚拟地(通过基类指针或引用)调用任何ff 的所有查找都找到派生最多的 f
  • Virtual 和 const 并不真正适用于该问题,但我将它们保留为标签,因为我没有看到太大的危害并且不需要包含更相关的标签。
  • 我同意virtual,但const 是整个问题的意义所在。覆盖f() 隐藏f() const

标签: c++ inheritance virtual constants name-lookup


【解决方案1】:

我将(再次)链接这个伟大的article

首先,[编译器]查看 直接范围,在这种情况下 C类的范围,并列出 它可以找到的所有功能 命名为 f (无论他们是否 访问甚至采取权利 参数数量)。 只有当它 不做然后继续 “向外”进入下一个封闭 范围 [...]

所以是的,fconst 版本是隐藏的,这很正常。正如 Simone 所指出的,您可以使用using 语句将A::f 带入C 范围内。

【讨论】:

  • +1 好文章。作为一个兴趣点,这也在 Effective C++ 第三版的第 33 条中进行了讨论
【解决方案2】:

是的,是的。你可以写:

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++ 草案文档(您可以找到 here)第 10.2.(3-4) 章。

【讨论】:

  • +1 用于参考标准,但我从icecrime 的回答中获得了更多信息。我很清楚using 选项。我对编译它不感兴趣,而是对理解语言感兴趣。
  • 谢谢Ari,但请记住,您的问题可能会被有同样问题的人咨询,他们也想编译。多写比少写要好,你同意吗? :)
【解决方案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 导入给定名称。这条规则可能令人惊讶,我不确定它是否对今天的语言有净好处,但他们没有问我——当时,我可能只能用两个音节的词来回答他们。 :)

【讨论】:

    【解决方案4】:

    插入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

    【讨论】:

    • 没有编译。也许你的意思是“A::f”。
    猜你喜欢
    • 2014-08-13
    • 1970-01-01
    • 2012-10-26
    • 2012-06-19
    • 1970-01-01
    • 1970-01-01
    • 2016-01-24
    • 1970-01-01
    • 2018-01-28
    相关资源
    最近更新 更多