覆盖不共享公共接口的基类虚函数

13
#include <iostream>
struct B1
{
    virtual void method()=0;
    virtual ~B1(){}
};

struct B2
{
    virtual void method()=0;
    virtual ~B2(){}
};

struct D: B1, B2
{
    virtual void method()
    {
        std::cout << "D::method\n";
    };
};

int main(int argc,char *argv[])
{
    D d;
    B1 &b1=d;
    B2 &b2=d;
    b1.method();
    b2.method();
    return 0;
}

注意,B1和B2没有共同的接口。

这样合法吗?如果可以,符合哪个标准?C++98/03/11?

MSVC和GCC都已经编译成功。

之前我认为,在这种情况下必须使用一些共同的接口(可能是虚继承)。

这种情况有特殊的名称吗?

它如何详细工作?也许有一些ISO参考资料?


1
如果你解释一下为什么认为这是一个特殊情况,可能会有人能够给出一个好的解释。 - Johan Lundberg
因为在这种情况下,我希望在D中有两个不同的独立继承虚函数:B1::method和B2::method。 - John
2个回答

7
您的代码格式正确: void D::method() 重写了 void B1::method()void B2::method()
规范说明(C++11 §10.3/2)如下:
如果在基类 Base 和派生自 Base 的类 Derived 中声明了具有相同名称、参数类型列表、cv 限定符和 ref 限定符(或缺少这些内容)的虚成员函数 vf,则声明具有与 Base::vf 相同名称、参数类型列表、cv 限定符和 ref 限定符(或缺少这些内容)的成员函数 vfDerived::vf 也是虚拟的(无论是否这样声明),它将覆盖 Base::vf

B1 声明了一个虚函数 void B1::method()。类 D 派生自 B1,并且还声明了一个与之相同的成员函数名(method)、相同的参数列表(没有参数)、相同的 cv 修饰符(没有修饰符)和相同的 ref 修饰符(没有修饰符)的成员函数。
因此,void D::method() 覆盖了 void B1::method()
对于 void B2::method(),相同的逻辑也适用(只需在上述说明中将 B1 替换为 B2),因此 void D::method() 覆盖了 void B1::method()void B2::method()

谢谢,这听起来是正确的。我已经在C++03和C++98中找到了类似的项目。但是,我很好奇,标准中是否有对这种情况的补充?也许有一些关于多重继承的特殊说明吗? - John
除了“和引用限定符”,我预计C++98和C++03的规范应该是相同的。 - James McNellis
太好了,这很好知道。顺便说一下,只有当D通过基类引用/指针访问时,才允许使用方法()而不覆盖它。比较:https://dev59.com/UHA75IYBdhLWcg3waIQJ#3310948 - Johan Lundberg
1
您的解释很有道理,但仍然令人惊讶。相同的代码体同时实现了两种不同的方法,因为B1::method和B2::method肯定是无关的。 - Mark Ransom
我希望这不会让一些编译器开发者感到惊讶。 - John
显示剩余2条评论

1

据我所知,这在每个标准中都是合法的。我不确定它是否有自己特殊的名称,但它类似于diamond problem

如果你在D中重写了"virtual void method()",那么你同时重写了B1和B2中的方法。

编辑:

回答为什么在D中没有"两个不同的独立继承的虚函数:B1::method和B2::method":

当覆盖一个方法时,你只能指定函数名、返回类型和参数,但你不能在覆盖时添加关于从哪个函数继承的签名细节。

想象一下如果可能的话,那么它可能看起来像这样:

struct D: B1, B2
{
    virtual void B1::method()
    {
        std::cout << "D::method\n";
    };
    virtual void B2::method()
    {
        std::cout << "D::method\n";
    };
};

但是看到这个,你已经可以说没有可能有那样的东西,因为在调用时

objectD.method()

你无法指定你正在调用哪一个。因此,即使有一种方法可以重载两个函数,仍然存在在函数调用上区分的问题。

编辑: “无法指定你正在调用哪一个。”是指,你无法指定是否要调用B2 :: method的D类重载或B2方法本身。objectD.B2 :: method将始终调用B2(未重载)方法(在这种情况下不会编译,因为B2没有实现)


谢谢,我明白了。我想知道更多细节,因为这对我来说有点令人惊讶。 - John
@James McNellis 那不仅仅只能在类成员内部才能实现吗?void B1method(){ B1::method } ? - cppanda
我知道。不过它们无论如何都会调用基类函数,而不考虑重载。 - cppanda
想象一下如果可能,它可能看起来像这样: 然后你得到一个名为 B1::method 的函数,其限定名称为 D::B1::method!但在C++中,D::B1 意味着 B1!当函数的 非限定 名称为 B1::method 时,你就知道有问题了。所以即使有一种同时重载它们的方式, 这也不会是重载,因为函数具有不同的名称:B1::method vs B2::method! 无论你如何尝试,我只是认为你无法使这个想法起作用。(但尝试是有趣的。;) - curiousguy

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