最底层的原始问题。
我想我明白你们现在在说什么 - 因为成员函数指针的内部结构是编译器/机器特定的,所以我真的不可能这样做。因此,即使它在我测试它时也能正常工作 - 我无法保证它会在其他编译器/机器上运行。
还有其他方法可以解决我想要的问题吗?
我有一个模板类和该类的基本模板类,我有一个委托类,它包含委托类在调用时应该调用的所有事件的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
中,我正在为地图生成我的密钥,如下所示:std::pair<int, int>( (int)((int*)object), (int)(static_cast<const void*>(&method)) )
。
method
是一个函数(方法)指针,object
是指向方法对象的指针。
它有效,但我有一种偷偷摸摸的怀疑,即我得到钥匙的第二部分的方式并不完全正确。
我从来没有完全理解函数指针,但我想我得到的是指针的地址而不是函数的地址,编译器不会让我这样做((int)(static_cast<const void*>(method)))
。
所以我的问题是 - 我如何从函数指针中获取一个唯一的键,如果我以后从另一个指向相同方法的函数指针获取键,它将是相同的?
提前致谢, 马丁
答案 0 :(得分:3)
第二个是不合法的:正式地,你不能将指针转换为
函数指向数据的指针(而void*
是指向数据的指针)。
此外,您无法保证能够将任何指针转换为
int
;如果int
至少与a一样大,则转换才合法
指针(这意味着您的代码应该无法在大多数64上编译
比特系统)。
有几种方法可以解决这个问题。首先,在大多数(全部?)现代
机器,函数的poitners和数据做的指针都是一样的
规模和代表性。 (事实上,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
是你的指针
对象和功能。)
请注意,不对指向成员函数的指针有效。 指向成员函数的指针是完全不同的野兽,我不这样做 认为有任何有效的方法可以将它们用作这种方式的关键 (因为它们可能并且经常会包含填充或未设置的字段 某些情况)。
就此而言,正式而言,即使是正常情况,也不能保证这一点 指针。该标准允许指针不关心位,或用于 几个不同的指针表示来比较相等。在 但是,在大多数(所有?)现代机器上都可以安全使用。
答案 1 :(得分:2)
你写得很好:
reinterpret_cast<uintptr_t>(method)
[edit] - 对于方法的指针,你将不得不继续使用c风格的演员表,如下所述:reinterpret_cast to void* not working with function pointers
&amp;方法就像你指向指针的suspec一样,所以它不是你想要的
uintptr_t优于int,因为它保证与指针的大小相同