用非纯虚函数重载纯虚函数版本

9

定义Base和Derived如下:

class Base {

    public:
        virtual int f1(int a) const = 0;
        virtual int f2(int a, int b) const { return a+b;}
};

class Derived : public Base {

    public:
        int f1(int a) const { return a; }
}; 

int main() {
    Derived obj;
    cout << obj.f1(1) << endl;
    cout << obj.f2(1, 2) << endl;
}

结果是:
1
3

obj.f1(1)使用来自Derivedf1实现,而obj.f2(1, 2)使用从Base继承的实现,这正是我想要的。

现在,我想让这两个函数具有相同的名称f,因此基类在有两个参数时提供了一个实现,派生类必须实现单参数版本(这就是为什么它是纯虚拟的)。

然而,如果我这样做(只需将f1f2重命名为f):

class Base {

    public:
        virtual int f(int a) const = 0;
        virtual int f(int a, int b) const { return a + b;}
};

class Derived : public Base {

    public:
        int f(int a) const { return a; }
};

int main() {
    Derived obj;
    cout << obj.f(1) << endl;
    cout << obj.f(1, 2) << endl;
}

I get the following error:

20:23: error: no matching function for call to 'Derived::f(int, int)'
20:23: note: candidate is:
14:13: note: virtual int Derived::f(int) const
14:13: note:   candidate expects 1 argument, 2 provided

为什么呢?难道不能进行这种重载吗?

@StoryTeller 的确是这样。当我进行研究时,我认为它与其中一个函数是纯虚函数有关,所以我想这就是为什么我没有找到那个问题的原因。这个答案:https://dev59.com/EHI-5IYBdhLWcg3w48xO#1629074 给出了一个很好的解释 名称隐藏 现象。 - Milo
5个回答

10

你需要编写代码

class Derived : public Base {

    public:
        using Base::f;
        int f(int a) const { return a; }
};

请注意using语句。它将基类版本重新引入作用域中。

8
现在,我希望这两个函数有相同的名称,即f
你需要编写:
class Derived : public Base {

    public:
        using Base::f;
        int f(int a) const { return a; }
};

请注意using语句。这会将基类版本带回作用域。[感谢@Bathsheba]
为什么?不可能进行此类重载吗?
不行,原始问题中写道由于[basic.scope.hiding¶3]
在成员函数定义中,块作用域名称的声明会隐藏具有相同名称的类成员的声明;参见[basic.scope.class]。派生类中成员的声明会隐藏具有相同名称的基类成员的声明;参见[class.member.lookup]。
该条款涉及名称而非重载。因此,无论基类中是否有其他重载,它们都共享相同的名称,根据上述引文被隐藏。

2
请随意将我的答案复制到此处顶部;这样读起来会更好。 - Bathsheba
现在读起来非常好 - 给你点赞! - Bathsheba
1
@Bathsheba 我们是否也应该给你的回答点个踩,以更加支持你呢? :) - lubgr
哈。@lubgr:随便说吧 - 它不如这个答案好。 - Bathsheba
@Bathsheba "这个答案没有用"显然不适用于你在10秒内解决了OP的问题的帖子。 - lubgr
@lubgr:你真好;-) - Bathsheba

1
您可以通过写入 using Base::f; 将所有Base定义引入范围,或者对于某些版本的f,您可以明确地编写类似于此的内容:int f(int a, int b) const override {return Base::f(a,b);}
class Derived : public Base {

public:
    int f(int a) const { return a; }
    int f(int a, int b) const override {return Base::f(a,b);} 
};

在这个答案中已经提到了使用的版本:

class Derived : public Base {

    public:
        using Base::f;
        int f(int a) const { return a; }
};

注意: 我知道第二个版本在MSVC 2010上不起作用。我也知道这个编译器已经过时了,但是只是为了那些关心的人 ;)


0
除了其他解决方案之外,如果您不想重新定义f,您可以使用显式调用基类的f
int main() {
    Derived obj;
    cout << obj.f1(1) << endl;
    cout << obj.Base::f2(1, 2) << endl;
}

-2

你也可以将它们全部设置为纯虚函数,然后在派生类中重写它们。这样你就可以按照自己的意愿继续使用你的类。


这个答案的问题在于你让子类承担了额外的工作来实现f2()。原始的f2()不是纯虚函数,可能是因为基本实现对许多(大多数?)用例已经足够了,但是希望留出空间供后代覆盖它,如果有必要的话。 - Andre Kostur
@AndreKostur,你在这一点上完全正确。 - Ibrahima Keita

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