c++ - 函数重载的可见性在基类和派生类之间分离

标签 c++ inheritance visitor-pattern

我正在尝试重构一些访问者模式代码以删除一些代码重复。这项任务的关键是将现有 API 的函数重载分成两部分:一些进入基类,而另一些进入扩展该基类的派生类。

在尝试在基类和派生类之间拆分 API 时,我遇到了意外的编译错误。试图抛开与访问者相关的背景,我将我的问题理清/提炼到下面的示例 (c++11) 代码中:

#include <iostream>

class X
{
public:
  virtual const char * name() const =0;
};

class Y : public X
{
public:
  virtual const char * name() const override { return "Y"; }
};

class Z : public X
{
public:
  virtual const char * name() const override { return "Z"; }
};

class APIBase // The API is split between this base class...
{
public:
  virtual void foo(Y & y) =0;
};

class APIDerived : public APIBase // ..and this derived class
{
public:
  virtual void foo(Z & z) =0;
};

class APIImplementation : public APIDerived
{
public:
  virtual void foo(Y & y) override {
    std::cout << "foo(" << y.name() << ")" << std::endl;
  }
  virtual void foo(Z & z) override {
    std::cout << "foo(" << z.name() << ")" << std::endl;
  }
};

class A
{
public:
  APIDerived & api() { return m_api; }
private:
  APIImplementation m_api;
};

int main(int argc, char * argv[])
{
  Y y;
  Z z;
  A a;
  a.api().foo(y);
  a.api().foo(z);
  return 0;
}

基本思想是类 A 通过调用 api() 提供在 APIBase 和 APIDerived 中定义的 API 的实现,然后可以作用于从类 X 派生的对象,并根据它是 Y 还是 A 做一些不同的事情Z.

我希望这段代码在运行时给我以下输出:

foo(Y)
foo(Z)

但是,在编译这段代码时,gcc 给我以下错误:

intf.cpp: In function ‘int main(int, char**)’:
intf.cpp:57:16: error: no matching function for call to ‘APIDerived::foo(Y&)’
   a.api().foo(y);
                ^
intf.cpp:57:16: note: candidate is:
intf.cpp:30:16: note: virtual void APIDerived::foo(Z&)
   virtual void foo(Z & z) =0;
                ^
intf.cpp:30:16: note:   no known conversion for argument 1 from ‘Y’ to ‘Z&’

我可以通过两种方式编译此示例代码并给出预期的输出,但我不确定在编译器(或 C++ 标准)眼中它们与原始代码有什么区别。

1。通过将两个 foo() 纯函数声明放入 APIBase 或 APIDerived(但不在它们之间拆分)来重新组合 API,例如:

class APIBase
{
public:
  virtual void foo(Y & y) =0;
  virtual void foo(Z & z) =0;
};

2。更改类 A,使其派生自 APIImplementation 并放弃 api() 重定向调用,例如:

class A : public APIImplementation {};

int main(int argc, char * argv[])
{
  Y y;
  Z z;
  A a;
  a.foo(y);
  a.foo(z);
  return 0;
}

我不想这样做。我也不想放弃继承以支持模板。

我是 C++ 的新手:请您帮我理解为什么这个示例代码无法编译,如果可能,请提供不需要我执行上述步骤 (1) 或 (2) 的解决方法?

技术细节:

Platform: Centos 7, Linux 3.10.0-123.6.3.el7.x86_64
Compiler: gcc (GCC) 4.8.2 20140120 (Red Hat 4.8.2-16)

最佳答案

默认情况下,基类中的 f 和派生类中的 f 被视为重载,重载是他们之间没有完成。

编译器基本上是通过作用域向后走,以找到一个作用域,其中至少有一个项目具有它要查找的名称。然后它查看该范围内具有该名称的所有内容,并对它们进行重载解析。如果在外部范围内有同名的东西(例如,父类),它将包含在该重载决议中。

但是,您可以将继承的名称纳入范围:

struct base {
    void foo(int);
};

class derived : public base {
    using base::foo;

    void foo();
};

现在,如果您在派生类中调用 foo,派生类和基类中的 foo 将被视为重载集,因此正确的调用将基于您传递的参数(或未传递的参数)。

关于c++ - 函数重载的可见性在基类和派生类之间分离,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25901629/

相关文章:

c++ - 为什么 Mac 开发者工具将 g++ 链接到 clang,而不是 clang++?

c++ - 在 OpenCV 中使用 MouseCallback 绘图

c# - 如何将多态对象作为派生对象传递?

c++ - 访问者接口(interface)的私有(private)继承如何允许可访问对象访问访问私有(private)访问实现?

c++ - 对称访客模式

c++ - 持有大量共享状态的访问者类 : best way to implement reference semantics?

c++ - 如何并行化使用某些变量的函数

C++ 与在线 SQL 数据库交互?

c++ - 类继承和重新定义成员类型c++

Java继承-如何设置子类实例的值以供其他子类共享(相同)?