我有一个简单的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;
};
答案 0 :(得分:3)
std::map
和std::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_multimap
比std::unrodered_map
- 矢量更快。
PS - 评论者和@ gchen的答案告诉你的所有内容对于multimap-vs-unordered-multimap基本上都是有效的。你需要专门化std::hash
和两个结构&#39;内部是非常不同的。