C++:方法重载的奇怪行为

C++: Weird behavior on method overloading

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

为了加快代码阅读:概念是定义一个接口(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 时,所有编译和运行都按预期进行。

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

函数重载仅适用于同一范围内可见的函数:

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

  using ISomething::f;
  //~~~~~~~~~~~~~~~~~^
};

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

  using ASomething::f;
  //~~~~~~~~~~~~~~~~~^
};

两次编译失败的原因相同。当您覆盖一个虚拟成员函数时,您隐藏了另一个。在 ASomething:

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

f 上的名称查找找到 ASomething::f 并停止,它不会继续寻找其他重载。因为 ASomething::f 有两个参数,而你试图用一个参数调用它,错误。为了允许对基础 class 进行重载,您必须使用 using-declaration:

引入基础 class 成员函数
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) 可以编译。

问题不是 'overloading' 问题,而是隐藏了一个基础 class 函数(参见其他答案)

稍作修改的示例:

#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)
    {
        // `this->f(x)` fails: 
        ISomething& i = *this;
        return i.f(x) + i.f(y); // (3)
    }
};

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

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

因此,从界面调用f,解决了冲突。虽然,您应该考虑 using base::f,如其他答案中所建议的那样。