向下转换指向成员函数的指针

时间:2016-11-03 08:31:17

标签: c++ casting member-function-pointers

我想向下转换指向成员函数的指针,其中基类是虚拟继承的。这会在编译时导致错误。

在我的应用程序中,我有几个对象的集合(例如D1D2),它们具有公共基类B。它们保持在一起并通过std::vector<B*>跟踪。

派生对象引入double - 返回需要定期调用的函数。基类通过std::vector<double(B::*)(void)>跟踪这些函数。整个链用于主函数的倒数第二行。

因此,我需要在将它们添加到向量时强制转换函数指针,就像在派生类D1D2的构造函数中所做的那样。如果是D1,则成功,但D2不成功,因为BD2的虚拟基础。据我所知,静态强制转换在这种情况下不起作用(错误消息1),因为派生类D21D22只有一个指向B实例的指针。但是,dynamic_cast也会在编译时导致错误(错误消息2),表示目标不是指针类型。

  

错误消息1 :错误:指向成员转换的指​​针,通过虚拟基地'B'v.push_back(static_cast(&amp; D2 :: f2));

     

错误消息2 :错误:不能dynamic_cast'&amp; D2 :: f2'(类型'double(类D2 :: )()')键入'double(类) B :: )()'(目标不是指针或引用)

我想知道,如果有的话,我可以执行所需的演员阵容。如果不是,我想知道为什么这样的事情是不可能的。

编辑:一些背景:我在数字上集成了一个常微分方程组。派生类表示系统的各个方程,每个派生类计算自己的状态。通过收集向量o中的类,我可以快速修改方程组。出于输出目的,需要基类中的向量v。在集成期间,每个子系统还通过基类处理自己的输出。钻石出现是因为我将两个经常使用的代码分成两个单独的类。

可以说,还有其他方法来设计类层次结构,还有其他方法可以实现我的目标(在这种情况下,我在p虚拟中创建了函数B,并重新实现了它在派生类中)。但是,我无法理解为什么演员会失败,所以我问了这个问题。

MWE:

    #include <vector>
    #include <algorithm>
    #include <iostream>

    // Base class
    class B {
    protected:
      std::vector<double(B::*)(void)> v;
    public:
      void p ( void )
      {for_each(v.begin(), v.end(), [this] (double (B::*f) (void)) {std::cerr << (this->*f)() << std::endl;});};
    };

    // Normal inheritance
    class D1: public B
    {public:
      double f1 ( void ) { return 1; }
      D1() { v.push_back(static_cast<double(B::*)(void)>(&D1::f1));} // Casting is successful here.
    };

    // Setting up 'the diamond'
    class D21: virtual public B {};
    class D22: virtual public B {};
    class D2: public D21, public D22
    {public:
      double f2 ( void ) { return 2; }
      D2() { v.push_back(dynamic_cast<double(B::*)(void)>(&D2::f2)); } // How to cast here?
    };

    int main ()
    {
      // Vector holding the classes together
      std::vector<B*> o;

      // Set up the system
      D1 d1;
      D2 d2;
      o.push_back(&d1);
      o.push_back(&d2);

      // Derived functions are called
      for_each(o.begin(),o.end(),[] (B *o) {o->p();});

      return 0;
    }

1 个答案:

答案 0 :(得分:3)

我认为应该可以实现您尝试使用的强制转换。然而,这是一个特别困难的角落案件,这可能是它不被允许的原因:

您可以将成员指针视为对象(非虚拟案例)或vtable(虚拟案例)的偏移量。只要修复了对象的相关布局,这就可以正常工作。但是,对于虚拟基础,对象内虚拟基础的位置可能取决于具体的子类。更具体地说,从D2派生的类可能使其虚拟基数与D2本身的偏移量不同。

现在,当您将成员指针强制转换为相对于其某个基类的指针时,需要调整指针。这通常是相对偏移的简单添加,它是编译时常量。但由于在D2内虚拟基址的位置在编译时是未知的,因此调整需要首先动态发现此偏移量。

正如我所说,这并非不可能实施。但是,它增加了编译器中指针调整代码的复杂性,只是为了支持极其罕见的极端情况。我猜这就是为什么不允许这样做的原因。