【问题标题】:Multiple Inheritance Virtual Call Ambiguity多重继承虚调用歧义
【发布时间】:2015-07-20 16:34:45
【问题描述】:

手头的问题很难描述,因此代码放在前面以便更清楚。

struct Base
{
    int b;
    virtual void foo(){cout << b << endl;}
    Base(int x) : b(x){}
};

struct Derived1 : Base    //not virtual
{
    virtual void foo(){/*Derived2's code*/}
    Derived1() : Base(1){}
};

struct Derived2 : Base    //not virtual
{
    virtual void foo(){/*Derived2's code*/}
    Derived2() : Base(2){}
};

struct MultiInheritance : Derived1, Derived2
{
    void bar1()
    {
        //needs to access Derived1's Base foo()
    }
    void bar2()
    {
        //needs to access Derived2's Base foo()
    }
};

假设在一些奇怪的奇怪场景中,我想要一个基类MultiInheritance,它有两个基类Derived1 和Derived2,它们有一个共同的非虚拟基类@987654325 @。

MultiInheritance 中有两个Base,如何指定我希望在MultiInheritance 中访问哪个Base 类?

上面的代码通过多次转换似乎可以正常工作,但我不确定这是否是已定义的行为。如果是,编译器如何实现它来满足多态性的需求?一方面,virtual 调用应该都产生相同的virtual 函数表,但另一方面,如果它这样做,它不会输出不同的答案。

编辑

我想强调Base 类必须是非虚拟的

EDIT2

深表歉意,我严重歪曲了自己。上面的代码更新后更好地反映了我原来的问题。

【问题讨论】:

  • 你认为哪个正确Base?或者你的问题是如何做到只有一个Base?
  • 您的分析是正确的,编译器将构建一个正确的 vtable,反映两个基类的存在。为避免这种情况,您需要虚拟继承,并且 vtable 结构将变得更加复杂。
  • 您的示例中没有歧义,它的行为与书中应有的完全一样。 “虚拟表”是一个实现细节。没有人说每个实现每个类必须有一个 vtbl,或者每个 vtable 中的每个函数签名必须有一个条目。事实上,对于任何使用 vtables 的实现来说,上述至少一项很可能是不正确的。
  • 这被称为“可怕的死亡钻石”并非空穴来风。只是不要这样做。如果出于某种奇怪的原因,你觉得不得不在任何地方开枪,就说static_cast&lt;Derived1*&gt;(this)。
  • @EduardoLeón“语义不明,有很多特殊情况”如果这不是“本质上不好”,那么我不知道是什么。 “在 OCaml 中” OCaml 中的菱形(和多重)继承与 C++ 完全不同。从字面上看,没有共同点。

标签: c++ polymorphism multiple-inheritance


【解决方案1】:

这被称为钻石问题。

http://www.cprogramming.com/tutorial/virtual_inheritance.html

如果您想保持基础非虚拟并获得您现在正在寻找的行为,您可以在 MultipleInheritance 中按照以下方式执行此操作,以确保您从正确的基类调用 foo() 函数

struct MultiInheritance : Derived1, Derived2
{
    void bar1()
    {
       Derived1::foo();
    }
    void bar2()
    {
        Derived2::foo();
    }
};

【讨论】:

    【解决方案2】:

    这是一个更具说明性的示例。

    #include <iostream>
    using namespace std;
    
    template <typename Res, typename Arg>
    Res& as(Arg& arg)
    {
      return arg;
    }
    
    struct Base
    {
        virtual void foo() = 0;
    };
    
    struct Derived1 : Base {};
    struct Derived2 : Base {};
    
    struct MoreDerived1 : Derived1
    {
        void foo() { cout << "Derived1\n"; }
    };
    
    struct MoreDerived2 : Derived2
    {
        void foo() { cout << "Derived2\n"; }
    };
    
    struct MultiInheritance : MoreDerived1,  MoreDerived2
    {
        void bar1() { as<Derived1>(*this).foo(); }
        void bar2() { as<Derived2>(*this).foo(); }
    };
    
    int main ()
    {
         MultiInheritance m;
         m.bar1();
         m.bar2();
    }
    

    这个例子说明:

    1. 您无需使用完整的继承路径明确指定您需要哪个 Base,下至具有明确 Base 子对象的子对象就足够了
    2. 虚函数机制在这里起作用。如果您尝试拨打Derived1::foo(),它将无法正常工作。

    as 辅助函数只是一个语法糖,你也可以这么说

    Derived1& d = *this;
    d.foo();
    

    【讨论】:

      【解决方案3】:

      MultiInheritance中有两个Base,如何指定哪个Base 我希望在 MultiInheritance 中访问的类?

      您对调用的基础对象有歧义

      void MultiInheritance::bar1(){
           foo();
      }
      

      解决这个问题的方法是告诉编译器去哪里寻找 foo。

      void MultiInheritance::bar1(){
           Derived1::foo();  // The same foo() as in your question.
      }
      

      这就是你所完成的

      void MultiInheritance::bar1()
      {
          Derived1& d = *this;
      

      这在标准 §10.2.12 中有所描述。这是很好定义的。就像你的连锁店

      void MultiInheritance::bar1()
      {
          Derived1& d = *this;
          Base& b = *this;
      

      同款。

      不幸的是,范围解析运算符无法让您从 MultiInhteritance 直接跳转到 Base as

      MultiInheritance::foo(){
           Derived1::Base::foo();
      

      正在描述一个嵌套类Base。

      要访问属于 Base 的 foo(),您可以使用 MultiInheritance 以及 Derived1 和 Derived2 中的范围解析语法。

      Derived1()::foo(){
          Base::foo; 
      

      如果这不合适,那么您提出的选项就是剩下的选项。

      如果是,编译器如何实现以满足需求 多态性?一方面,虚拟呼叫都应该导致 相同的虚函数表,但另一方面,如果它这样做,它不会 输出不同的答案。

      编译器实现因编译器而异,正如评论者所说:将 vtables 用于虚函数是一个实现细节。如果一个实现使用虚拟函数的 vtable,实现需要考虑这种情况。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-02-09
        • 2011-10-14
        • 2016-05-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多