原始问题在底部。
我想我现在明白你们在说什么了——因为成员函数指针的内部结构是特定于编译器/机器的,所以我真的不可能做到我想做的。因此,即使它在我测试时有效——我也不能保证它在其他编译器/机器上也会有效。
那么还有其他方法可以实现我想要的吗?
我有一个模板类和该类的基模板类,我有一个委托(delegate)类,其中包含委托(delegate)类在调用时应调用的所有事件的 std::map。
我需要一个映射的原因是,既要确保同一个成员函数(指向成员函数的事件)不会被添加更多次,又要使使用对象和成员函数从映射中删除事件成为可能最初在实例化事件对象时使用。
template <class T_arg1> struct EventBase1
{
public:
bool operator < (const EventBase1 &event1) const { return _key < event1._key; };
virtual void operator()(T_arg1 t1) const {};
std::pair<intptr_t, intptr_t> _key;
};
template <class T, class T_arg1> struct Event1: public EventBase1<T_arg1>
{
template <class T_arg1> friend class Delegate1;
typedef typename void (T::*Method)(T_arg1);
private:
Method _method;
T* _object;
public:
Event1(T* object, Method method): _object(object), _method(method)
{
_key = std::pair<intptr_t, intptr_t>(reinterpret_cast<intptr_t>(object), reinterpret_cast<intptr_t>( reinterpret_cast<void*&>(method)));
};
virtual void operator()(T_arg1 t1) const {
(_object->*_method)(t1);
};
};
template <class T_arg1> class Delegate1
{
public:
typedef typename EventBase1<T_arg1> T_event;
void operator += (T_event* observer)
{
assert(observer);
_observers[*observer] = observer;
};
void operator -= (const T_event &observer)
{
std::map<T_event, T_event*>::iterator i = _observers.find(observer);
if(i != _observers.end()) {
delete i->second;
_observers.erase(i);
}
};
void operator()(T_arg1 t1)
{
for(std::map<T_event, T_event*>::iterator i = _observers.begin(); i != _observers.end(); i++) {
(*(i->second))(t1);
}
};
private:
std::map<T_event, T_event*> _observers;
};
原始问题:
我将函数指针存储在 std::map
中,我正在为 map 生成 key ,如下所示:std::pair<int, int>( (int)((int*)object), (int)(static_cast<const void*>(&method)) )
.
method
是函数(方法)指针,object
是指向方法对象的指针。
它有效,但我偷偷怀疑我获取 key 第二部分的方式并不完全正确。
我从来没有完全理解函数指针,但我猜我得到的是指针的地址而不是函数的地址,编译器不会让我这样做((int)(static_cast<const void*>(method)))
.
所以我的问题是 - 我如何从函数指针获取唯一键,如果我稍后从另一个指向相同方法的函数指针获取键,该键将相同?
提前致谢, 马丁
最佳答案
第二个是不合法的:正式地,你不能将指针转换为
指向数据指针的函数(void*
是指向数据的指针)。
此外,您不能保证能够将任何指针转换为
整数
;仅当 int
至少与 a 一样大时,转换才合法
指针(这意味着你的代码应该无法在大多数 64
位系统)。
有几种解决方法。首先,在大多数(所有?)现代
机器、函数指针和数据指针确实具有相同的
规模和代表性。 (事实上,Posix 需要它。即使它
在我使用的第一台 Unix 机器上情况并非如此。)如果我们假设这样,
您可以通过使用 intptr_t
来保证足够大的整数类型,并且
使用额外的间接级别“欺骗”编译器:
std::pair<intptr_t, intptr_t>(
reinterpret_cast<intptr_t>( reinterpret_cast<void*&>( object ) ),
reinterpret_cast<intptr_t>( reinterpret_cast<void*&>( method ) ) )
(假设 object
和 method
是指向
对象和函数。)
请注意,这对指向成员函数的指针不起作用。 指向成员函数的指针是完全不同的野兽,我不 认为有任何有效的方法可以以这种方式将它们用作 key (因为它们可能并且经常包含填充或未设置的字段,在 某些情况下)。
就此而言,正式来说,即使是正常情况下也不能真正保证这一点 指针。该标准允许指针具有无关位,或者 几个不同的指针表示比较相等。在 然而,在实践中,它在大多数(所有?)现代机器上是安全的。
关于C++ 将函数指针转换为唯一的 “hash” 键,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11983070/