55

可能重复:
C++ 重载决议

我遇到了一个问题,在我的类覆盖其基类的一个函数后,所有重载版本的函数都被隐藏了。这是设计使然还是我只是做错了什么?

前任。

class foo
{
  public:
    foo(void);
    ~foo(void);
    virtual void a(int);
    virtual void a(double);
};

class bar : public foo 
{
  public:
    bar(void);
    ~bar(void);
    void a(int);
};

下面会给出一个编译错误,说明 bar 中没有 a(double) 函数。

main() 
{
  double i = 0.0;
  bar b;
  b.a(i);
}
4

3 回答 3

84

在类栏中,添加

using foo::a;

这是 C++ 中常见的“陷阱”。一旦在类作用域中找到名称匹配,它就不会在继承树上进一步查找重载。通过指定 'using' 声明,您将所有 'a' 的重载从 'foo' 带入 'bar' 的范围。然后重载工作正常。

请记住,如果存在使用 'foo' 类的现有代码,则其含义可能会因额外的重载而改变。或者额外的重载可能会引入歧义,并且代码将无法编译。詹姆斯霍普金的回答指出了这一点。

于 2009-05-20T14:34:46.653 回答
26

这就是语言过去的工作方式。在using关键字之前,如果您覆盖了一个重载函数,则必须将它们全部重载:

class bar : public foo 
{
  public:
    bar(void);
    ~bar(void);
    a(int);
    a(double d) { foo::a(d); }  // add this
}

这让很多人感到恼火,语言委员会增加了使用功能,但一些旧习惯很难改掉;和习惯者†有一个很好的论据。

正如詹姆斯霍普金斯指出的那样,通过添加using,程序员表达了派生类将在没有警告的情况下将任何未来的 foo::a() 覆盖添加到其可接受的签名列表中的意图。

这是他描述的一个例子:

#include <iostream>
class Base {
public:
  virtual void f(double){ std::cout << "Base::Double!" << std::endl; }
  // virtual void f(int) { std::cout << "Base::Int!" << std::endl; } // (1)
  virtual ~Base() {}
};

class Derived : public Base {
public:
  // using Base::f; // (2)
  void f(double) { std::cout << "Derived::Double!" << std::endl; }
};

int main(int, char **) {
  Derived d;
  d.f(21);
  return 0;
}

输出将是“Derived::Double!” 因为编译器会将整数参数提升为双精度。g++ 4.0.1 -Wall 不会警告此促销已发生。

取消注释 (1) 以模拟对 Base 添加方法 Base::f(int) 的未来更改。即使使用 -Wall 和“Derived::Double!”,代码也会再次编译,而不会发出警告。仍然是输出。

现在取消注释 (2) 以模拟 Derived 程序员决定包括所有 Base::f 签名。代码编译(没有警告),但输出现在是“Base::Int!”。

† 我想不出一个英语单词来形容“那些有习惯的人”和“上瘾的人”太强了。

于 2009-05-20T14:31:59.717 回答
14

这是设计使然。重载解决方案仅限于单个范围。当将附加函数添加到基类或命名空间范围时,它可以防止一些令人讨厌的有效代码更改含义的情况。

于 2009-05-20T14:38:26.380 回答