如何专门化无<Key>序容器中用户定义类型的 std::hash::operator()?
How to specialize std::hash<Key>::operator() for user-defined type in unordered containers?
支持在std::unordered_set<Key>
和std::unordered_map<Key, Value>
中自定义键类型必须提供operator==(Key, Key)
和哈希函子:
struct X { int id; /* ... */ };
bool operator==(X a, X b) { return a.id == b.id; }
struct MyHash {
size_t operator()(const X& x) const { return std::hash<int>()(x.id); }
};
std::unordered_set<X, MyHash> s;
只写std::unordered_set<X>
会更方便对于X
类型,使用默认散列,比如编译器和库自带的类型。参考
- c++标准草案N3242§20.8.12和§17.6.3.4 [Hash。要求),
- 提振。无序
- g++
includec++4.7.0bitsfunctional_hash.h
- vc10
includexfunctional
- Stack Overflow中的各种相关问题
似乎可以专门化std::hash<X>::operator()
:
namespace std { // argh!
template <>
inline size_t
hash<X>::operator()(const X& x) const { return hash<int>()(x.id); } // works for MS VC10, but not for g++
// or
// hash<X>::operator()(X x) const { return hash<int>()(x.id); } // works for g++ 4.7, but not for VC10
}
考虑到编译器对c++ 11的支持还处于实验阶段——我没有尝试Clang——,以下是我的问题:
将这样的专门化添加到命名空间
std
是否合法?我对此百感交集。哪个
std::hash<X>::operator()
版本(如果有的话)是符合c++ 11标准的?有便携的方法吗?
明确允许并鼓励您向命名空间std
*添加专门化。添加哈希函数的正确(基本上也是唯一)方法是:
namespace std {
template <> struct hash<Foo>
{
size_t operator()(const Foo & x) const
{
/* your code here, e.g. "return hash<int>()(x.value);" */
}
};
}
(您可能考虑支持的其他流行专门化是std::less
, std::equal_to
和std::swap
。)
*)只要涉及的类型之一是用户定义的,我想。
我打赌是在unordered_map/unorder_set/…的Hash模板参数上。类:
#include <unordered_set>
#include <functional>
struct X
{
int x, y;
std::size_t gethash() const { return (x*39)^y; }
};
typedef std::unordered_set<X, std::size_t(*)(const X&)> Xunset;
typedef std::unordered_set<X, std::function<std::size_t(const X&)> > Xunset2;
int main()
{
auto hashX = [](const X&x) { return x.gethash(); };
Xunset my_set (0, hashX);
Xunset2 my_set2(0, hashX); // if you prefer a more flexible set typedef
}
当然
- hashX也可以是全局静态函数 在第二种情况下,您可以传递
- 老式函数对象(
struct Xhasher { size_t operator(const X&) const; };
) -
std::hash<X>()
- 任何满足签名的绑定表达式-
- 老式函数对象(
@Kerrek SB已经覆盖了1)和3).
2)尽管g++和VC10用不同的签名声明std::hash<T>::operator()
,但这两个库的实现都是符合标准的。
标准没有指定std::hash<T>
的成员。它只是说,每个这样的专门化必须满足std::unordered_set
的第二个模板参数所需的相同的"哈希"需求,以此类推。即:
- 哈希类型
H
是一个函数对象,至少有一个参数类型Key
。 -
H
是可复制的 -
H
是可破坏的 - 如果
h
是类型为H
或const H
的表达式,并且k
是类型可转换为(可能是const
)Key
的表达式,则h(k)
是类型为size_t
的有效表达式。 - 如果
h
是类型为H
或const H
的表达式,u
是类型为Key
的左值,则h(u)
是类型为size_t
的合法表达式,不修改u
。
相关文章:
- 如何获取std::result_of函数的返回类型
- 如何在c++迭代器类型中包装std::chrono
- 在C++中对T*类型执行std::move的意外行为
- 检查 std::shared_ptr<> 的当前底层类型是否为 T
- 在一个模板函数中,若输入的类型是enum类,我该如何使用std::underlying_type
- 为什么 std::optional::operator=(U&&) 要求你是非标量类型?
- std::visit无法识别类型
- 哪些类型可以转换为std::any
- 从类型std::函数传递变量失败,尽管调用方期望的类型完全相同
- ";结果类型必须是可从输入范围的值类型""构造的;创建std::vector时
- <Windows>为什么 std::thread::native_handle 返回类型为"long long unsigned int"的值,而不是 void*(又名 HANDLE)?
- r语言 - C++ 类型为"const std ::?
- C++中"std::sort"比较器的不同类型
- C++11 中不同类型的对象的 std::array 的替代方案
- 检查某些类型是否是模板类 std::optional 的实例化
- clion 无法解析 std 类型
- 使用 std 类型的 ADL 无法找到运算符
- 为什么 std 类型不提供来自分配器不同源的转换构造函数/赋值
- 哪些 std 类型在移动构造函数中用作 arg 后保证为空/空
- Qt -混合Qt和std:: c++类型