有没有办法将派生自同一碱基的不同类型的 std::函数存储在单个向量中?

Is there a way to store std::functions of different types derived from the same base in a single vector?

提问人:guyus15 提问时间:10/13/2022 更新时间:10/13/2022 访问量:70

问:

我知道以前有人问过这样的问题,但我还没有找到适合我具体情况的答案。

这里是:我正在尝试构建一个事件系统,程序员可以在其中创建事件,这些事件可以由事件管理器广播并由事件侦听器监听。每个事件都派生自一个公共基类。GameEvent

#pragma once

#include <string>

struct GameEvent
{
    std::string name = "Game Event";
};

// Events
struct StartEvent : GameEvent
{
    std::string msg = "This is the start event!";
};

struct EndEvent : GameEvent
{
    std::string msg = "This is the end event";
};

struct Events
{
    static const StartEvent s_StartEvent;
    static const EndEvent s_EndEvent;
};

const StartEvent Events::s_StartEvent;
const EndEvent Events::s_EndEvent;

// Custom hash function for GameEvent.
struct GameEventHash
{
    std::size_t operator()(const GameEvent& evt) const
    {
        return std::hash<std::string>()(evt.name) << 1;
    }
};

// Custom equal function for GameEvent.
struct GameEventEqual
{
    bool operator()(const GameEvent& t1, const GameEvent& t2) const {
        return t1.name == t2.name;
    }
};

为了将与事件的关联存储到它们的侦听器,我正在尝试使用 s 到 s 向量的无序映射。这可以看作是类的静态成员。GameEventstd::functionEventManager

#pragma once

#include "events.h"

#include <functional>
#include <unordered_map>
#include <vector>

struct EventManager
{
    template<typename T>
    static void AddListener(std::function<void(T)> listener);

    static void Broadcast(GameEvent evt);
private:
    static std::unordered_map<GameEvent, std::vector<std::function<void(GameEvent)>>, GameEventHash, GameEventEqual> s_Events;
};

inline void EventManager::Broadcast(GameEvent evt)
{
    for (std::function<void(GameEvent)> listener : s_Events[evt])
    {
        listener(evt);
    }
}

template<typename T>
void EventManager::AddListener(std::function<void(T)> listener)
{
    T evt;

    // Add function to map of events to listeners.
    auto iter = s_Events.find(evt);

    // If event already has listeners
    if (iter != s_Events.end())
    {
        // Add to the vector of listeners.
        std::vector<std::function<void(GameEvent)>>& listeners = s_Events[evt];
        listeners.push_back(listener);
    }
    else
    {
        // Insert new vector with listener.
        s_Events[evt] = std::vector<std::function<void(GameEvent)>>{listener};
    }

}

std::unordered_map<GameEvent, std::vector<std::function<void(GameEvent)>>, GameEventHash, GameEventEqual> EventManager::s_Events;

我遇到的问题是类的方法。因为事件的无序映射的向量采用以下类型的函数 ,尝试在模板中使用任何其他类型都会导致错误。有问题的错误是:AddListenerEventManagerGameEvent

EventSystem\EventSystem\event-manager.h(40,12): error C2664: 'void std::vector<std::function<void (GameEvent)>,std::allocator<std::function<void (GameEvent)>>>::push_back(const _Ty &)': cannot convert argument 1 from 'std::function<void (StartEvent)>' to 'const _Ty &'

EventSystem\EventSystem\event-manager.h(45,62): error C2440: '<function-style-cast>': cannot convert from 'initializer list' to 'std::vector<std::function<void (GameEvent)>,std::allocator<std::function<void (GameEvent)>>>'

我了解我的问题,但我不明白如何解决它。有人可以提供解决方案或替代建议吗?

C++ 模板 STL 事件处理 STD

评论

2赞 Drew Dormann 10/13/2022
EventManager::AddListener在目前的状态下似乎相当嘈杂和多余。该功能的全部内容可以实现为s_events[T{}].push_back(listener);
0赞 nick 10/13/2022
这里的关键字是指针的向上转换:将 StartEvent* 转换为 GameEvent*,然后传递它。如果需要与对象交互,请为基类提供子级重写的纯虚拟方法。
0赞 Sailanarmo 10/13/2022
我也想知道那个 if 语句中发生了什么。向量在哪里定义?它不是每次执行 if 时都会被创建,然后在超出范围后死亡吗?listeners
0赞 guyus15 10/13/2022
@nick 这不会导致对象切片吗?
1赞 guyus15 10/13/2022
@Sailanarmo 侦听器向量只是对映射中向量之一的引用

答: 暂无答案