我正在做以下事情:
它工作正常(到目前为止),但我应该保留它吗?
当前代码说明:
EventsWarehouse用于存储和调用事件:
#include <iostream>
#include <functional>
#include <unordered_map>
class EventsWarehouse
{
public:
typedef std::tuple<AView*, void (AView::*)()> box_t;
typedef std::unordered_multimap<std::string, box_t> boxes_t;
void storeEvent(std::string const &event, AView *v, void (AView::*callback)())
{
this->_events.insert(std::make_pair(event, std::make_tuple(v, callback)));
return ;
}
template<typename... Args>
bool fireEvent(std::string const &event, Args... args)
{
auto it = this->_events.find(event);
AView *v;
void (AView::*callback_)();
void (AView::*callback)(Args...);
for (; it != this->_events.end(); it++)
{
v = std::get<0>(it->second);
callback_ = std::get<1>(it->second);
/*
** CAST #2
** <void (AView::*)()>
** to
** <void (AView::*)(std::string, int, double)>
** before call
*/
callback = reinterpret_cast<void (AView::*)(Args...)>(callback_);
(v->*callback)(args...);
}
return (true);
}
private:
boxes_t _events;
};
查看存储在上述类中的类:
class AView
{
protected:
AView(){}
};
class DerivedView : public AView
{
public:
void fooCallback(std::string s, int i, double d)
{
std::cout << "DerivedView::fooCallback received " << s << ", " << i << ", " << d << std::endl;
return ;
}
};
主:
int main(void)
{
DerivedView dv;
EventsWarehouse ewh;
/*
** CAST #1
** <void (DerivedView::*)(std::string, int, double)>
** to
** <void (AView::*)()>
** for storing purpose
*/
ewh.storeEvent("event 1", &dv, reinterpret_cast<void (AView::*)()>(&DerivedView::fooCallback));
ewh.fireEvent("event 1", std::string("Hello World"), 42, 84.42);
return (0);
}
答案 0 :(得分:1)
根据草案n4296 for C ++ 11规范,5.2.10重新解释cast [expr.reinterpret.cast]§10
如果T1和T2都是函数类型或两种对象类型,则可以将“指向类型T1的X成员的指针”类型的prvalue显式转换为不同类型的prvalue“指向类型为T2的Y的成员的指针” .72 null成员指针值(4.11)被转换为目标类型的空成员指针值。除以下情况外,此转换的结果未指定:
- 将“指向成员函数的指针”类型的prvalue转换为指向成员函数的不同指针 键入并返回其原始类型会生成指向成员值的原始指针。
- 将“指向类型T1的X的数据成员的指针”类型的prvalue转换为“指向数据的指针”类型 类型为T2的Y的成员“(其中T2的对齐要求不比T1更严格) 并返回其原始类型产生指向成员值的原始指针。 转换为指向没有参数的成员函数的指针,并返回到具有正确参数的成员函数,应该返回原始指针。
恕我直言,问题在于fooCallback
仅在DerivedView
类上定义,因此它是不类AView
的成员函数。< / p>
这是正确的:
void (AView::*p)() = reinterpret_cast<void (AView::*)()>(&DerivedView::fooCallback);
void (DerivedView::*callback)(std::string, int, double) =
reinterpret_cast<void (DerivedView::*)(std::string, int, double)>(p);
v->callback("Hello World"), 42, 84.42);
提供v
是指向AView *
DerivedView
但是当您将void (DerivedView::*)(std::string, int, double)
转换为void (AView::*)(std::string, int, double)
时,它们的类型不同,因此转换未指定
它有效,因为非静态非虚拟成员函数的常见实现只是一个隐藏参数为this
的普通(非成员)函数。因此,指向成员的指针只存储该函数的地址,并使用指向DerivedView
的指针正确调用它,从而得到预期的结果。但是不同的实现也可以存储实际类型并引发异常(或做任何其他事情)。
TL / DR:当您从void (DerivedView::*)(std::string, int, double)
转换为void (AView::*)(std::string, int, double)
时,您不会将指向成员的指针转换为其原始类型并调用未定义的行为。