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

Downcasting a pointer to member function

我想向下转换一个指向成员函数的指针,其中基本 class 是虚拟继承的。这会导致编译时出错。

在我的应用程序中,我有几个对象(例如 D1D2)的集合,它们具有共同的基础 class B。它们被放在一起并通过 std::vector<B*>.

进行跟踪

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

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

error message 1: error: pointer to member conversion via virtual base 'B' v.push_back(static_cast(&D2::f2));

error message 2: error: cannot dynamic_cast '&D2::f2' (of type 'double (class D2::)()') to type 'double (class B::)()' (target is not pointer or reference)

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

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

可以说,还有其他方法可以设计 class 层次结构,也有其他方法可以实现我的目标(在这种情况下,我将 p 中的函数 B 虚化,并在派生 classes 中重新实现它)。然而,我无法理解为什么转换失败,所以我提出了这个问题。

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;
    }

我相信应该可以实现您尝试使用的转换。然而,这是一个特别困难的极端情况,这可能是不允许的原因:

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

现在,当您将成员指针转换为相对于其基 classes 之一的指针时,需要调整指针。这通常是相对偏移量的简单加法,它是一个编译时间常量。但是由于 D2 中虚拟基址的位置在编译时是未知的,调整需要首先动态发现这个偏移量。

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