我想向下转换指向成员函数的指针,其中基类是虚拟继承的。这会在编译时导致错误。
在我的应用程序中,我有几个对象的集合(例如D1
,D2
),它们具有公共基类B
。它们保持在一起并通过std::vector<B*>
跟踪。
派生对象引入double
- 返回需要定期调用的函数。基类通过std::vector<double(B::*)(void)>
跟踪这些函数。整个链用于主函数的倒数第二行。
因此,我需要在将它们添加到向量时强制转换函数指针,就像在派生类D1
和D2
的构造函数中所做的那样。如果是D1
,则成功,但D2
不成功,因为B
是D2
的虚拟基础。据我所知,静态强制转换在这种情况下不起作用(错误消息1),因为派生类D21
和D22
只有一个指向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;
}
答案 0 :(得分:3)
我认为应该可以实现您尝试使用的强制转换。然而,这是一个特别困难的角落案件,这可能是它不被允许的原因:
您可以将成员指针视为对象(非虚拟案例)或vtable(虚拟案例)的偏移量。只要修复了对象的相关布局,这就可以正常工作。但是,对于虚拟基础,对象内虚拟基础的位置可能取决于具体的子类。更具体地说,从D2
派生的类可能使其虚拟基数与D2
本身的偏移量不同。
现在,当您将成员指针强制转换为相对于其某个基类的指针时,需要调整指针。这通常是相对偏移的简单添加,它是编译时常量。但由于在D2
内虚拟基址的位置在编译时是未知的,因此调整需要首先动态发现此偏移量。
正如我所说,这并非不可能实施。但是,它增加了编译器中指针调整代码的复杂性,只是为了支持极其罕见的极端情况。我猜这就是为什么不允许这样做的原因。