使用unordered_map将对象映射为键

时间:2018-04-27 15:30:40

标签: c++ c++11 observable

我有一个简单的Observable类,实现了观察者模式。此类将模板类型Event映射到已注册的观察者。这很好,但是出于性能原因,我想使用std :: unordered_map代替std :: map。

如果我将下面的成员变量更改为使用unordered_map,我会得到一个相当普遍的错误:

std::map<Event, std::vector<std::function<void()>>> _observers;
  

Static_assert失败“指定的哈希不符合哈希   要求“

我的期望是std :: map和std :: unordered_map应该是可以互换的。在这种情况下使用unordered_map有哪些散列要求,为什么它不同?

这是我的代码:

#include <functional>
#include <unordered_map>
#include <map>
#include <vector>
#include <utility>

template <typename Event>
class Observable
{
public:
    Observable()=default;

    template <typename Observer>
    void registerObserver(const Event &event, Observer &&observer)
    {
        _observers[event].push_back(std::forward<Observer>(observer));
    }

    template <typename Observer>
    void registerObserver(Event &&event, Observer &&observer)
    {
        _observers[std::move(event)].push_back(std::forward<Observer>(observer));
    }

    void notify(const Event &event) const
    {
        for (const auto& obs : _observers.at(event)) obs();
    }

    /* disallow copying */
    Observable(const Observable&)=delete;
    Observable& operator=(const Observable&)=delete;

private:
    std::map<Event, std::vector<std::function<void()>>> _observers;
};

2 个答案:

答案 0 :(得分:3)

std::mapstd::unordered_map不可互换。它们根本不同。

std :: map是使用自平衡二进制搜索树实现的,要形成BST,您需要定义密钥的比较方式(按顺序排列)。例如,std::map中的默认比较函数为std::less或基本上为operator<。所以你的Event类型必须定义operator<(成员函数或非成员函数)。但是,如果需要,可以通过在第三个模板参数中指定比较函数,将比较函数更改为其他函数。

e.g。

std::map<Event, std::vector<std::function<void()>>, MyComp<Event>> _observers;

myComp可以是具有有效签名的任何合适的函数对象(函子,自由函数,lambda函数)。 e.g

template <typename Event>
struct MyComp{
    bool operator()(const Event& lhs, const Event& rhs) const {
        ...
    }
};

另一方面,使用哈希表实现std::unordered_map。就像它的名字一样,它们是无序的,因此它们不需要比较功能来工作。但是他们需要知道如何将一个键(默认值为std::hash)散列为unsigned int值(即size_t),以及如何判断两个键是否相同(默认值为{{ 1}})。

如果operator==是某些用户定义的类型,则Event将无效。结果,无法创建std::hash<Event>。但是,您可以应用与上面的MyComp相同的逻辑,并创建一个通用的事件哈希函数对象。例如

std::unordered_map

如果你还想定义一个广义的相等函数(即不使用Event类型的template <typename Event> struct MyHash { std::size_t operator()(const Event& key) const { ... } }; ),你可以做同样的事情。

operator==

然后将template <typename Event> struct MyEqual { bool operator() (const Event& lhs, const Event& rhs) const { ... } }; 定义为

unordered_map

当然,std::unordered_map<Event, std::vector<std::function<void()>>, MyHash<Event>, MyEqual<Event>> _observers; MyHash的正文应该足够通用,以便它可以适用于您想要使用的所有或大多数MyEqual类型。

答案 1 :(得分:2)

  

而不是std::map我希望出于性能原因使用std::unordered_map

这还不够好,std::unordered_map仍然很慢;看我的this answer和那里的链接。

但还有更多:实际上, 任何通用地图结构对您来说都是错误的,从根本上考虑了实施质量。您看,由于每个事件可能包含多个观察者,因此相关数据结构为std::multimap,或者对于无序情况,为std::unordered_multimap。我无法说明实施质量,但我敢打赌,使用std::unordered_multimapstd::unrodered_map - 矢量更快。

PS - 评论者和@ gchen的答案告诉你的所有内容对于multimap-vs-unordered-multimap基本上都是有效的。你需要专门化std::hash和两个结构&#39;内部是非常不同的。