部分继承重载虚函数集合

10

我原本以为我理解了继承、虚函数和函数重载,但是现在有一个问题,这些功能之间的相互作用似乎让我很困扰。

假设我有一个简单的基类,其中包含一个重载的虚函数,还有一个从它派生的第二个类:

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; }
};

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

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

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

但是当我尝试调用这个无参函数时:

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

IT出现错误!gcc显示“没有找到匹配的函数调用'd::f()';候选项为: virtual int d::f(int)”。clang则表示“函数调用参数太少,期望1个,实际0个;你是否指的是 'b::f'?”尽管d继承自b,后者具有一个0参数的f()方法,编译器仍忽略了这一点,并试图调用d的1参数方法。

我可以通过在派生类中重复定义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;

然而我的问题是,我违反了什么规则?这个规则试图强制执行/保护/捍卫什么?我认为我在这里尝试做的正是我认为继承应该用于的事情。


这是一个非常好的关于该主题的讨论:https://dev59.com/EHI-5IYBdhLWcg3w48xO - user3477273
2个回答

7

这被称为名称隐藏

当您在派生类中声明具有相同名称的函数时,所有基类中具有相同名称的函数都会被隐藏。

为了获得对它们的不限定访问权限,请在派生类中添加一个using声明:

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

1
它符合标准吗? - Othman Benchekroun
3
在嵌套的声明区域或派生类中,通过显式声明相同名称的名字可以隐藏该名称。 - TartanLlama
@OthmanBenchekroun 在13.2声明匹配中,与Steve给出的相同示例(或几乎相同)。 - marom
谢谢!我现在看到我的问题相当普遍,例如 Stack Overflow herehere,以及 C++ FAQ 列表 here。但我仍然想知道...为什么?这只是为了让编译器更容易处理,还是这种继承被认为是一个坏主意? - Steve Summit
@SteveSummit 你可能可以在更正式的来源中找到更多信息,我没有深入思考过,但我猜这是为了防止您在派生类中继承您正在尝试阻止的功能而自己给自己惹麻烦。此外,如果没有这个规则,你怎么说“我不想让这个基类函数可见”呢? - TartanLlama
@ TartanLlama:很好的观点,关于说“不”。还有其他人指向了一个由Lance Roberts提供的很好的解释,在这个问题中。 - Steve Summit

2
除了@TartanLlama的回答外,还有一些解释:
当编译器需要解析对f的调用时,它按顺序执行以下三个主要步骤:
1.名称查找。在执行任何其他操作之前,编译器会搜索至少具有一个名为f的实体的作用域,并列出候选项。在本例中,名称查找首先在d的作用域中查找是否至少有一个名为f的成员;如果没有,则依次考虑基类和封闭命名空间,直到找到至少有一个候选项的作用域。但是,在这种情况下,编译器查找的第一个作用域已经有一个名为f的实体,然后名称查找停止。
2.重载解析。接下来,编译器执行重载解析以从候选列表中选择唯一的最佳匹配项。在本例中,参数计数不匹配,因此失败。
3.可访问性检查。最后,编译器执行可访问性检查,以确定是否可以调用所选函数。
参考名称查找

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接