问题描述
我正在执行以下操作:
- 通过 3个参数来获取成员函数指针 strong>派生的类。
- 将其从基类(具有 0个参数)投射到成员函数指针。
- 将其三个参数放回基类。
- 调用它。
- Taking a member function pointer with 3 params from a derived class.
- Casting it to a member function pointer from the base class with 0 params.
- Casting it to the base class with the 3 params back.
- Calling it.
工作正常(到目前为止),但我应该保留吗?
It works fine (so far), but should i keep it?
当前代码的描述:
EventsWarehouse用于存储和调用事件:
EventsWarehouse is used to store and invoke events:
#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;
};
查看存储在上述类中的类:
View classes stored in the above class:
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);
}
推荐答案
根据C的n4296草案++ 11规范5.2.10重新解释类型转换[expr.reinterpret.cast]§10
According to draft n4296 for C++11 specification, 5.2.10 Reinterpret cast [expr.reinterpret.cast] §10
-将类型为指向成员函数的指针的prvalue转换为指向成员函数$ b的不同指针$ b类型并返回其原始类型将产生指向成员值的原始指针。
— converting a prvalue of type "pointer to member function" to a different pointer to member function type and back to its original type yields the original pointer to member value.
-将类型将指针指向类型T1的数据成员的prvalue转换为类型指向类型T2的Y的数据
成员的指针(其中T2的对齐要求不比T1的严格)
并返回其原始类型将产生指向成员值的原始指针。
转换为没有参数的成员函数指针,再返回具有正确参数的成员函数,应该返回原始指针。
— converting a prvalue of type "pointer to data member of X of type T1" to the type "pointer to data member of Y of type T2" (where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer to member value. the conversion to a pointer to member function with no parameters and back to a member functions with correct parameters should give back original pointer.
恕我直言,问题在于 fooCallback
仅在 DerivedView
类上定义,因此它是不是类 AView
的成员函数。
IMHO, the problem is that fooCallback
is only defined on DerivedView
class, and as such it is not a member function of class AView
.
这是正确的:
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)
它们是不同的类型,因此转换是未指定的
But as you end converting a void (DerivedView::*)(std::string, int, double)
to a void (AView::*)(std::string, int, double)
they are different type so conversion is unspecified
它可以工作,因为非静态非虚拟成员函数的通用实现只是普通(非成员)函数,其隐藏参数为 this
。因此,指向成员的指针仅存储该函数的地址,并使用指向 DerivedView
的指针正确调用它,以提供预期的结果。但是不同的实现也可以存储实际类型并引发异常(或执行其他任何操作)。
It works, because common implementation for non static non virtual member functions is simply a normal (non member) function with a hidden parameter being this
. So the pointer to member just stores the address of that function and correctly calls it with a pointer to a DerivedView
giving expected result. But a different implementation could also store the actual type and raise an exception (or do anything else).
TL / DR:最后,您从<$ c进行了转换$ c> void(DerivedView :: *)(std :: string,int,double)到 void(AView :: *)(std :: string,int,double )
您不会将其成员强制转换为指向其原始类型的指针并调用未定义的行为。
TL/DR: As you end with a conversion from void (DerivedView::*)(std::string, int, double)
to void (AView::*)(std::string, int, double)
you do not cast to pointer to member to its original type and invoke undefined behaviour.
这篇关于成员函数指针的转换,从Derived到Base类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!