我正在尝试模仿 JavaScript 风格 Event/EventTarget
C++ (C++17) 中的操作。
我有一个Event
根据需要工作的基类,以及 KeyboardEvent
继承自 Event
的类。
我的EventTarget.addEventListener
功能商店EventObjects
在可以用 dispatchEvent
调用的 vector 中.
我可以使用addEventListener
使用 Event
存储回调函数参数,例如onGenericEvent(Event event)
。
但是,我无法使用addEventListener
添加采用 KeyboardEvent
的回调函数参数,例如onKeyDown(KeyboardEvent event)
.
我在期待std::function<void(Event)>
接受KeyboardEvent
因为它继承自 Event
但我收到编译错误:
ERROR: cannot convert 'void (*)(KeyboardEvent)' to 'std::function<void(Event)>'
我应该如何进行?
#include <iostream>
#include <functional>
#include <string>
#include <vector>
class Event {
public:
Event(std::string _type, void* _pointer) {
type = _type;
pointer = _pointer;
}
std::string type;
void* pointer;
};
struct EventObject {
std::string type;
std::function<void(Event)> listener;
void* pointer;
};
class EventTarget {
public:
static void addEventListener(std::string type, std::function<void(Event)> listener, void* pointer){
EventObject eventObj;
eventObj.type = type;
eventObj.listener = listener;
eventObj.pointer = pointer;
events.push_back(eventObj);
}
static void dispatchEvent(Event event){
for (unsigned int n = 0; n < events.size(); ++n) {
if (event.type == events[n].type && event.pointer == events[n].pointer) {
events[n].listener(event);
}
}
}
static std::vector<EventObject> events;
};
std::vector<EventObject> EventTarget::events;
class KeyboardEvent : public Event {
public:
KeyboardEvent(std::string _type, void* _pointer) : Event(_type, _pointer) {
key = "random key";
}
std::string key;
};
class App
{
public:
static void onGenericEvent(Event event) {
printf("event.type = %s \n", event.type.c_str());
}
static void onKeyDown(KeyboardEvent event) {
printf("event.type = %s \n", event.type.c_str()); // print Event class property
printf("event.key = %s \n", event.key.c_str()); // print KeyboardEvent class property
}
};
int main() {
App* app = new App();
EventTarget::addEventListener("generic", &App::onGenericEvent, app);
Event event("generic", app);
EventTarget::dispatchEvent(event); // simulate generic event
EventTarget::addEventListener("keydown", &App::onKeyDown, app); //ERROR: cannot convert 'void (*)(KeyboardEvent)' to std::function<void(Event)>'
KeyboardEvent keyEvent("keydown", app);
EventTarget::dispatchEvent(keyEvent); // simulate keydown event
}
最佳答案
注意:首先,您要对 Event
进行切片对象,如果你像这样按值传递它们。您需要传递引用或指针才能使多态性发挥作用!
void(KeyboardEvent&)
比void(Event&)
更具体 ,所以std::function<void(Event&)>
无法包裹 void(KeyboardEvent&)
.
考虑一下如果可以的话,那么这是可能的:
void handler(KeybaordEvent& evt)
{
std::cout << evt.key;
}
int main()
{
std::function<void(Event&)> callback(&handler);
MouseEvent mouseEvent; // Imagine MouseEvent also derives from Event
callback(mouseEvent);
}
自 MouseEvent
是 Event
一个std::function<void(Event&)>
可以接受MouseEvent
范围。如果允许包裹 void(KeyboardEvent&)
这将导致处理程序传递错误的类型。因此,你想要的东西是不被允许的。
请记住,类型检查是在编译时完成的。只是因为您(尝试)通过检查 EventObject
永远不会传递错误的类型的type
运行时的成员并不意味着您可以在编译时拥有不匹配的类型。
有两种方法可以解决这个问题:
- 让您的所有处理程序接受
Event
并进行运行时检查,确保他们获得了预期的事件类型(即使用dynamic_cast
)。 - 使用模板确保在编译时始终将正确类型的事件传递给正确的处理程序。
作为第二个选项的示例,您可以这样:
template <typename EventType>
struct EventObject {
std::string type;
std::function<void(EventType)> listener;
void* pointer;
};
class EventTarget {
public:
template <typename EventType>
static void addEventListener(std::string type, std::function<void(EventType)> listener, void* pointer){
EventObject<EventType> eventObj;
eventObj.type = type;
eventObj.listener = listener;
eventObj.pointer = pointer;
events<EventType>.push_back(eventObj);
}
template <typename EventType>
static void dispatchEvent(EventType event){
for (auto& eventObj : events<EventType>) {
eventObj.listener(event);
}
}
template <typename EventType>
static std::vector<EventObject<EventType>> events;
};
template <typename EventType>
std::vector<EventObject<EventType>> EventTarget::events;
class KeyboardEvent : public Event {
public:
KeyboardEvent(std::string _type, void* _pointer) : Event(_type, _pointer) {
key = "random key";
}
std::string key;
};
class App
{
public:
static void onGenericEvent(Event event) {
printf("event.type = %s \n", event.type.c_str());
}
static void onKeyDown(KeyboardEvent event) {
printf("event.type = %s \n", event.type.c_str()); // print Event class property
printf("event.key = %s \n", event.key.c_str()); // print KeyboardEvent class property
}
};
int main() {
App* app = new App();
EventTarget::addEventListener<Event>("generic", &App::onGenericEvent, app);
Event event("generic", app);
EventTarget::dispatchEvent(event); // simulate generic event
EventTarget::addEventListener<KeyboardEvent>("keydown", &App::onKeyDown, app); //ERROR: cannot convert 'void (*)(KeyboardEvent)' to std::function<void(Event)>'
KeyboardEvent keyEvent("keydown", app);
EventTarget::dispatchEvent(keyEvent); // simulate keydown event
}
这会创建单独的events
每种类型事件的 vector 。现在,编译器可以在编译时检查是否始终将正确的事件类型传递给每个处理程序。
关于c++ - std::function:无法将派生类参数转换为基类参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75845709/