unordered_map / unordered_set 中元组的通用哈希
为什么 std::unordered_map
tuple
定义哈希函数是很乏味的,例如
template<> struct do_hash<tuple<int, int>>
{ size_t operator()(std::tuple<int, int> const& tt) const {...} };
用元组作为键构建无序映射 (Matthieu M.) 展示了如何
为 boost::tuple
自动执行此操作。无论如何,是否可以在不使用可变参数模板的情况下对 c++0x 元组执行此操作?
当然这应该在标准中:(
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
这适用于 gcc 4.5,允许所有包含标准可哈希类型的 c++0x 元组成为以下成员
unordered_map
和unordered_set
不用多说。(我将代码放入头文件中,然后将其包含在内。)
该函数必须位于 std 命名空间中,以便由
参数相关名称查找 (ADL)。
有更简单的解决方案吗?
标准一致性代码
Yakk 指出,在 std 命名空间中专门化事物实际上是未定义的行为。如果您希望拥有一个符合标准的解决方案,那么您需要将所有这些代码移至您自己的命名空间中,并放弃任何 ADL 自动查找正确哈希实现的想法。而不是 :
您需要:
其中
hash_tuple
是您自己的命名空间,而不是std::
。为此,您首先必须在
hash_tuple
命名空间内声明一个哈希实现。这会将所有非元组类型转发到std::hash
:确保
hash_combine
调用hash_tuple::hash
而不是std ::hash
然后包含所有其他先前的代码,但将其放入
namespace hash_tuple
而不是std::
This works on gcc 4.5 allowing all c++0x tuples containing standard hashable types to be members of
unordered_map
andunordered_set
without further ado.(I put the code in a header file and just include it.)
The function has to live in the std namespace so that it is picked up by
argument-dependent name lookup (ADL).
Is there a simpler solution?
Standard Conformant code
Yakk points out that specialising things in the std namespace is actually undefined behaviour. If you wish to have a standards conforming solution, then you need to move all of this code into your own namespace and give up any idea of ADL finding the right hash implementation automatically. Instead of :
You need:
where
hash_tuple
is your own namespace rather thanstd::
.To do this, you first have to declare a hash implementation inside the
hash_tuple
namespace. This will forward all non tuple types to thestd::hash
:Make sure that
hash_combine
callshash_tuple::hash
and notstd::hash
Then include all the other previous code but put it inside
namespace hash_tuple
and notstd::
对于 C++20,可以使用 折叠表达式 和 通用 lambda 用于计算元组的哈希值,无需递归。我更喜欢依赖
std::hash
而不是手动组合哈希:- 1
insizeof(uintmax_t) * 4 - 1
是可选的,但似乎稍微改善了哈希分布。该类可以与std::tuple
和std::pair
一起使用。With C++20, it is possible to use fold expressions and generic lambdas to compute hash of a tuple without recursion. I prefer to rely on
std::hash<uintmax_t>
instead of manually combining hashes:- 1
insizeof(uintmax_t) * 4 - 1
is optional, but appears to slightly improve hash distribution. This class can be used both withstd::tuple
andstd::pair
.在我的 C++0x 草案中,
20.8.15
表示 hash 专门用于内置类型(包括指针,但似乎并不意味着取消引用它们)。它似乎还专门用于error_code
、bitset
、unique_ptr
、shared_ptr< /code>、
类型索引
、字符串
、u16string
、u32string
、wstring
、vector
和thread::id
。 (令人着迷的列表!)我没有使用过 C++0x 变量,所以我的格式可能很差,但是沿着这些思路的东西可能适用于所有元组。
这个版本实际上可以编译并运行
Yakk 观察到直接专门化
std::hash
是 <从技术上来说是不允许的,因为我们正在专门使用一个不依赖于用户定义类型的声明的标准库模板。In my C++0x draft,
20.8.15
says hash is specialized for built-in types (including pointers, but doesn't seem to imply dereferencing them). It also appears to be specialized forerror_code
,bitset<N>
,unique_ptr<T, D>
,shared_ptr<T>
,typeindex
,string
,u16string
,u32string
,wstring
,vector<bool, Allocator>
, andthread::id
. (facinating list!)I've not used C++0x variadics, so my formatting is probably way off, but something along these lines might work for all tuples.
This version actually compiles and runs
Yakk has observed that specializing
std::hash
directly is technically not allowed, since we're specializing a standard library template with a declaration that does not depend on a user-defined type.如果你想以简单的方式做到这一点。只要做
If you want to do it in a simple way. Just do