【发布时间】:2013-12-10 15:54:59
【问题描述】:
所以我有一个变体类,我最近添加了存储指向成员函数数据的能力。它使用以下代码来完成。
class Variant
{
void* _value;
template <typename T1>
void Variant::ThisCall( T1* pThis )
{
typedef void(T1::* fptr)( );
fptr a;
int* iiVal = new (&a) int;
*iiVal = *((int*)_value);
(pThis->*a)( );
}
};
// usage
Variant myfunc = &SomeClass::SomeMethod;
SomeClass* s = new SomeClass( );
myfunc.ThisCall( s );
我为这个解决方案所做的最重要的事情是不能将指向成员函数的指针转换为 void*。所以赋值运算符本质上是这个操作的逆操作。它获取给定的数据,将其屏蔽为 int 指针(如果它本身是指针)并将 int 指针分配给 void*,这是完全合法的。
所以我的问题是:为什么我觉得这是一个糟糕的解决问题的方法?但是几天来我一直在这个问题上如此深入,以至于我看不到它。谢谢!
[编辑#1]
一位评论者指出,这可能不适用于虚拟方法。我已经使用以下代码进行了测试,似乎可以签出。
class ImplA : public Base
{
public:
virtual void Print( )
{
cout << "ImplA print\n";
}
};
class ImplB : public Base
{
public:
virtual void Print( )
{
cout << "ImplB print\n";
}
};
class ImplC : public ImplA
{
public:
virtual void Print( )
{
cout << "ImplC print\n";
}
};
// usage
Variant x = &Base::Print;
auto b = new ImplA; // replace ImplA with ImplB or ImplC and it works as expected
x.ThisCall( b );
对于一些附加信息,我使用 VS2010 作为我的编译器。谢谢!
[编辑#2]
为了提供上下文,我已经在这个变体类上工作了一段时间,并试图让它支持你可以扔给它的任何东西。在这样做的时候,我想到了函数指针和成员函数指针。然后我想出了这个,想知道这个解决方案到底有多可靠。转换和语法对我来说是第一个危险信号,但我认为,由于它所拥有的数据的差异,这只是与领域有关。但我仍然不相信这是应该如何工作的。
【问题讨论】:
-
一个问题: 可能不适用于pointer to a virtual member function。
void*不需要与 any 函数指针 IIRC 具有相同的大小。 -
IIRC 转换函数指针无效或导致 UB。
-
这太丑了,而且有充分的理由。你真的很可能不应该这样做。它适用于我遇到的所有编译器,但是如果在您的上下文中可能的话,使用虚拟方法和接口的深思熟虑的解决方案可能会更好。有时,在拦截现有函数时,您必须进行一些时髦的转换。根据您的编译器/系统,可能会有保证使这种安全,但它永远不会漂亮。编写新代码时,使用接口和虚拟调度。
-
@WilliamCustode 您的代码包含未定义的行为;它适用于某些编译器上的某些示例并不能证明它可以保证工作(对于更复杂的示例)。在这个简单的示例中,VS2010 中(虚拟)成员函数指针的大小似乎与
void*的大小相同,但是对于 g++ 而言,情况并非如此。即使是这样,您也违反了别名规则 [basic.lval]/10。 -
添加多重继承后,指向成员的指针的最小大小为8;使用虚拟继承,对于 VS2010,它将是 12。即使这样,如果您不介意鼻恶魔,一个简单的示例也可以产生“正确”的结果。
标签: c++ pointers member-function-pointers reinterpret-cast