函数重载的可见性在基类和派生类之间分配

时间:2014-09-17 22:52:05

标签: c++ inheritance visitor

我正在尝试重构一些访问者模式代码以删除一些代码重复。这个任务的关键是要求将现有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类提供APIBase中定义的API的实现,并通过调用api()进行APIDerived,然后可以对从X类派生的对象起作用,并根据它是Y还是a来做不同的事情。 ž。

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

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)的变通方法?< / p>

技术细节:

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)

1 个答案:

答案 0 :(得分:2)

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

编译器基本上会向后遍历范围,以找到一个范围,其中至少有一个项目具有它正在寻找的名称。然后它查看该范围内具有该名称的所有内容,并对它们进行重载解析。如果那里的某个外部作用域(例如,父类)具有相同的名称,则将包含在该重载解析中。

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

struct base {
    void foo(int);
};

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

    void foo();
};

现在,如果在派生类中调用foo,派生类和基类中的foo将被视为重载集,因此要调用的正确值将基于你传递的论点(或缺乏论证)。