代码之家  ›  专栏  ›  技术社区  ›  Puppy

C++中的虚拟继承与静态继承混合

  •  9
  • Puppy  · 技术社区  · 14 年前

    如果你有这样的东西:

    #include <iostream>
    
    template<typename T> class A
    {
    public:
        void func()
        {
            T::func();
        }
    };
    
    class B : public A<B>
    {
    public:
        virtual void func()
        {
            std::cout << "into func";
        }
    };
    
    class C : public B
    {
    };
    
    int main()
    {
      C c;
      c.func();
    
      return 0;
    }
    

    func()是动态调度的吗?
    如果B有一个虚拟重写,它是动态调度的,但是如果B没有,它是静态调度的,你怎么能实现类A呢?

    编辑:我的代码没有编译?抱歉,伙计们。我现在有点病。我的新代码也无法编译,但这是问题的一部分。另外,这个问题是给我的,不是常见问题。

    #include <iostream>
    
    template<typename T> class A
    {
    public:
        void func()
        {
            T::func();
        }
    };
    
    class B : public A<B>
    {
    public:
        virtual void func()
        {
            std::cout << "in B::func()\n";
        }
    };
    
    class C : public B
    {
    public:
        virtual void func() {
            std::cout << "in C::func()\n";
        }
    };
    class D : public A<D> {
        void func() {
            std::cout << "in D::func()\n";
        }
    };
    class E : public D {
        void func() {
            std::cout << "in E::func()\n";
        }
    };
    
    int main()
    {
      C c;
      c.func();
      A<B>& ref = c;
      ref.func(); // Invokes dynamic lookup, as B declared itself virtual
      A<D>* ptr = new E;
      ptr->func(); // Calls D::func statically as D did not declare itself virtual
      std::cin.get();
    
      return 0;
    }
    
    visual studio 2010\projects\temp\temp\main.cpp(8): error C2352: 'B::func' : illegal call of non-static member function
          visual studio 2010\projects\temp\temp\main.cpp(15) : see declaration of 'B::func'
          visual studio 2010\projects\temp\temp\main.cpp(7) : while compiling class template member function 'void A<T>::func(void)'
          with
          [
              T=B
          ]
          visual studio 2010\projects\temp\temp\main.cpp(13) : see reference to class template instantiation 'A<T>' being compiled
          with
          [
              T=B
          ]
    
    6 回复  |  直到 14 年前
        1
  •  2
  •   Fred Nurk    14 年前

    我不确定我是否理解你的要求,但看来你错过了关键的CRTP演员:

    template<class T>
    struct A {
      void func() {
        T& self = *static_cast<T*>(this);  // CRTP cast
        self.func();
      }
    };
    
    struct V : A<V> {  // B for the case of virtual func
      virtual void func() {
        std::cout << "V::func\n";
      }
    };
    
    struct NV : A<NV> {  // B for the case of non-virtual func
      void func() {
        std::cout << "NV::func\n";
      }
    };
    

    如果T没有声明它自己的func,这将是无限递归,因为self.func将找到一个<T>::func。即使T的派生类(例如下面的DV)声明了自己的func,但T没有声明,这也是正确的。

    使用不同的最终重写器进行测试,以显示公布的分派工作:

    struct DV : V {
      virtual void func() {
        std::cout << "DV::func\n";
      }
    };
    struct DNV : NV {
      void func() {
        std::cout << "DNV::func\n";
      }
    };
    
    template<class B>
    void call(A<B>& a) {
      a.func();  // always calls A<T>::func
    }
    
    int main() {
      DV dv;
      call(dv);   // uses virtual dispatch, finds DV::func
      DNV dnv;
      call(dnv);  // no virtual dispatch, finds NV::func
    
      return 0;
    }
    
        2
  •  1
  •   Ben Jackson    14 年前

    如果B有一个虚拟重写,它是动态调度的,但是如果B没有,它是静态调度的,你怎么能实现类A呢?

    有点矛盾,不是吗?A类用户可能对B或C一无所知。如果你提到A,唯一知道 func() 需要动态调度的是查询vtable。自从 A::func() 不是虚拟的,它没有入口,因此没有地方放置信息。一旦你使它虚拟化,你就可以咨询vtable,它是动态调度。

    获取直接函数调用(或内联)的唯一方法是使用非虚拟函数,并且不通过基类指针进行间接寻址。

    编辑:我认为Scala中的习惯用法是 class C: public B, public A<C> (用子类重复这个特性)但是这在C++中是不起作用的,因为它使成员 A<T> 模棱两可的 C .

        3
  •  1
  •   Mark Ransom    14 年前

    在您的特定示例中,不需要动态分派,因为 c 在编译时已知。对…的呼唤 B::func 将被硬编码。

    如果你打电话来 func 通过一个 B* ,则将调用虚拟函数。但在你精心设计的例子中 B::函数 再一次。

    A* 自从 A 是一个模板类-不能将 一个 ,只有一个绑定到特定子类。

        4
  •  1
  •   sbi    14 年前

    如果B有一个虚拟重写,它是动态调度的,但是如果B没有,它是静态调度的,你怎么能实现类A呢?

    正如其他人所注意到的,很难理解这个问题,但它让我想起了很久以前学过的东西,所以回答你的问题还有很长的路要走:

    template<typename Base> class A : private Base
    {
    public:
        void func()
        {
            std::count << "A::func";
        }
    };
    

    鉴于此,这取决于 A 的基础是否 func() 是虚拟的。如果 Base 宣布 virtual 那么它将是虚拟的 一个 也是。否则不会。请看:

    class V
    {
    public:
        virtual void func() {}
    };
    class NV
    {
    };
    
    class B : public A<V>  // makes func() virtual
    {
    public:
        void func()
        {
            std::count << "B::func";
        }
    };
    
    class C : public A<NV>  // makes func() non-virtual
    {
    public:
        void func()
        {
            std::count << "C::func";
        }
    };
    

    这会不会正好回答你的问题?

        5
  •  0
  •   Chubsdad    14 年前

    是否动态调度函数取决于两个因素:

    a)对象表达式是引用类型还是指针类型

    b)函数(重载解析解析为的函数)是否为虚函数。

    现在来看看你的代码:

      C c; 
      c.func();   // object expression is not of pointer/reference type. 
                  // So static binding
    
      A <B> & ref = c; 
      ref.func(); // object expression is of reference type, but func is 
                  // not virtual. So static binding
    
    
      A<D>* ptr = new D; 
      ptr->func(); // object expression is of pointer type, but func is not 
                   // virtual. So static binding 
    

    简而言之,“func”不是动态调度的。

    注意::禁止虚拟函数调用机制。

    $10.3/12-“明确限定 作用域运算符(5.1)抑制 虚拟的呼叫机制。

    OP2中的代码出错,因为只有当“Y”是“X”范围内的静态成员时,语法X::Y才能用于调用“X”范围内的“Y”。

        6
  •  -1
  •   Tony Delroy    14 年前

    似乎你只需要添加一些跟踪和用法来回答你自己的问题。。。

    #include <iostream>
    
    template<typename T> struct A { 
        void func() { 
            T::func(); 
        } 
    }; 
    
    struct B1 : A<B1> { 
        virtual void func() { 
            std::cout << "virtual void B1::func();\n";
        } 
    }; 
    
    struct B2 : A<B2> { 
        void func() { 
            std::cout << "void B2::func();\n";
        } 
    }; 
    
    struct C1 : B1 { }; 
    struct C2 : B2 { }; 
    
    struct C1a : B1 {
        virtual void func() {
            std::cout << "virtual void C1a::func();\n";
        }
    };
    
    struct C2a : B2 {
        virtual void func() {
            std::cout << "virtual void C2a::func();\n";
        }
    };
    
    int main()
    {
        C1 c1; 
        c1.func(); 
    
        C2 c2; 
        c2.func(); 
    
        B1* p_B1 = new C1a;
        p_B1->func();
    
        B2* p_B2 = new C2a;
        p_B2->func();
    }
    

    输出:

    virtual void B1::func();
    void B2::func();
    virtual void C1a::func();
    void B2::func();
    

    结论:A确实具有B's函数的虚性。