我需要解释为什么以下代码无法编译。我有一个解决方法,下面将详细说明,但我不了解原始版本的失败。

加快代码读取速度:概念是定义一个接口(interface)(ISomething),然后创建一个抽象实现(ASomething),该抽象实现使用第一个(尚未定义)一个(2)实现第二个函数(1)。从抽象方法(例如SomethingImpl)派生的完整实现必须定义第一个方法,并可以选择覆盖第二个方法。

#include <iostream>

class ISomething
{
public:
  virtual ~ISomething()
  { }
  virtual int f(int x) = 0; // (1)
  virtual int f(int x, int y) = 0; // (2)
};

class ASomething
  : public virtual ISomething
{
public:
  virtual int f(int x, int y) // (2)
  {
    return f(x) + f(y); // (3)
  }
};

class SomethingImpl
  : public ASomething
{
public:
  virtual int f(int x) // (1)
  {
    return x+1;
  }
};

int main()
{
  SomethingImpl a;
  std::cout << a.f(10) << std::endl; // (1)
  std::cout << a.f(10,20) << std::endl; // (2)
  return 0;
}

编译此代码会在Visual Studio 2013(Windows)和g++ 4.4.5(Linux)上产生错误。错误非常相似,我将仅详细介绍g++输出:
$ g++     SibFun.cpp   -o SibFun
SibFun.cpp: In member function ‘virtual int ASomething::f(int, int)’:
SibFun.cpp:18: error: no matching function for call to ‘ASomething::f(int&)’
SibFun.cpp:16: note: candidates are: virtual int ASomething::f(int, int)
SibFun.cpp:18: error: no matching function for call to ‘ASomething::f(int&)’
SibFun.cpp:16: note: candidates are: virtual int ASomething::f(int, int)
SibFun.cpp: In function ‘int main()’:
SibFun.cpp:36: error: no matching function for call to ‘SomethingImpl::f(int, int)’
SibFun.cpp:26: note: candidates are: virtual int SomethingImpl::f(int)
make: *** [SibFun] Error 1

我尝试在(3)上使用不同的表示法,例如return this->f(x) + this->f(y),但是在错误消息中我没有经历任何重大变化。

但是,当我将(3)更改为return ISomething::f(x) + ISomething::f(y);时,我只有:
$ g++     SibFun.cpp   -o SibFun
SibFun.cpp: In function ‘int main()’:
SibFun.cpp:36: error: no matching function for call to ‘SomethingImpl::f(int, int)’
SibFun.cpp:26: note: candidates are: virtual int SomethingImpl::f(int)
make: *** [SibFun] Error 1

但!当将(2)f更改为g时,所有编译和运行均会按预期进行。

这种行为背后的原因是什么?为什么我不能使用f名称作为(2)

最佳答案

两种编译失败的发生原因均相同。当您覆盖一个虚拟成员函数时,您将隐藏另一个。在ASomething中:

virtual int f(int x, int y) // (2)
{
    return f(x) + f(y); // (3)
}

f上进行名称查找会找到ASomething::f并停止,它不会继续寻找其他重载。由于ASomething::f接受两个参数,并且您尝试使用一个参数进行调用,因此错误。为了允许对基类进行重载,您必须使用using-declaration引入基类成员函数:
using ISomething::f; // NOW, ISomething::f(int ) is found by lookup

virtual int f(int x, int y)
{
    return f(x) + f(y);
}

同样,SomethingImpl需要using ASomething::f;语句,以便a.f(10)可以编译。

08-04 10:10