重载虚函数集的部分继承

partial inheritance of set of overloaded virtual functions

我以为我了解继承、虚函数和函数重载,但我有一个案例让我无法理解这些功能之间的相互作用。

假设我有一个包含重载虚函数的简单基础 class,以及从中派生的第二个 class:

class b {
 public:
    virtual int f() { return 1; }
    virtual int f(int) { return 2; }
};


class d : public b {
 public:
    virtual int f(int) { return 3; }
};

请注意,派生的 class d 仅覆盖了一个重载虚函数。

我可以实例化 class d 的对象并在其上调用 f(int),没问题:

d x;
std::cout << x.f(0) << std::endl;

但是当我尝试调用 0 参数函数时:

std::cout << x.f() << std::endl;

失败了! gcc 说 "no matching function for call to 'd::f()'; candidates are: virtual int d::f(int)"。 clang 说 "too few arguments to function call, expected 1, have 0; did you mean 'b::f'?" 即使 d 是从 b 派生的,它有一个 0 参数 f() 方法,编译器忽略了它,并试图调用 d的 1-argument 方法代替。

我可以通过在派生的 class:

中重复 0 参数函数的定义来解决这个问题
class d : public b {
 public:
    virtual int f() { return 1; }
    virtual int f(int) { return 3; }
};

或者,正如 clang 的错误消息所建议的,我可以使用一种我从未想过会起作用的愚蠢的消歧语法:

std::cout << x.b::f() << std::endl;

但我的问题是,我违反了什么规则,该规则试图达到什么目的 enforce/protect/defend?我认为我在这里尝试做的正是我认为继承的目的。

这被称为名称隐藏

当您在派生中声明同名函数时 class,基类中所有同名函数都将被隐藏。

为了获得对它们的无限制访问权限,请在派生的 class:

中添加 using 声明
class d : public b {
 public:
    using b::f;
    virtual int f(int) { return 3; }
};

除了@TartanLlama 的回答之外的一些解释:

当编译器必须解析对 f 的调用时,它按顺序执行三项主要操作:

  1. 名称查找。在执行任何其他操作之前,编译器会搜索至少具有一个名为 f 的实体的作用域,并生成候选列表。在这种情况下,名称查找首先在 d 的范围内查找,看看是否至少有一个名为 f 的成员;如果没有,基础 类 和封闭的命名空间 将依次考虑,一次一个,直到一个范围至少有一个 候选人被发现。不过,在这种情况下,编译器的第一个范围 查找已经有一个名为 f 的实体,然后名称查找停止。

  2. 超载分辨率。接下来,编译器执行重载决议来挑选 候选人名单中唯一的最佳匹配。在这种情况下,参数的计数不匹配,因此失败。

  3. 辅助功能检查。最后,编译器执行可访问性检查以确定是否可以调用所选函数。

参考 Name lookup