std::hash对指针类型特化本质是将地址转为size_t后做位运算扰动,只依赖地址不关心内容;相同地址哈希值恒定,不同地址尽量不同,因此不能用于按对象内容去重,需自定义哈希器和等价谓词。

std::hash 怎么算哈希值?
标准库对指针类型的 std::hash 特化,本质是把指针值(即地址)按整数对待,再做一次位运算扰动。它不关心指针指向什么,也不解引用,只取 reinterpret_cast<:size_t>(ptr)</:size_t> 的结果,然后通常右移若干位再异或自身(具体实现依赖 libstdc++ / libc++ / MSVC,但都保证:相同地址 → 相同哈希值;不同地址 → 尽量不同哈希值)。
这意味着:两个指向不同对象的指针,哪怕内容完全一样,只要地址不同,哈希就不同;而同一对象的指针,在生命周期内哈希值恒定。
为什么不能直接用 std::hash 哈希自定义类型指针?
你写 std::hash<myclass>{}</myclass> 能编译通过,是因为标准提供了 std::hash<void></void> 的泛化特化:所有指针类型都会退化到 void* 版本。所以不是“为 MyClass* 单独实现”,而是统一走 void* 路径 —— 也就是地址映射逻辑。
- 没有类型擦除开销,速度极快
- 不调用任何
MyClass的成员函数或 operator== - 若你误以为它在哈希对象内容,就会在
unordered_set<myclass></myclass>里发现“相同对象多次插入”——因为指针值不同(比如临时 new 出来又 delete,地址复用前不可预测)
常见错误:把指针哈希当对象内容哈希用
典型现象:unordered_set<foo></foo> 插入多个指向相同 Foo{42} 实例的指针,结果 size 变大;或者你期望两个等价对象的指针能被当作同一个 key,但实际没生效。
立即学习“C++免费学习笔记(深入)”;
原因在于:哈希值只由地址决定,operator== 对指针也只比较地址。如果你真要按对象内容去重,得自己写哈希器和等价谓词:
struct FooContentHash {
size_t operator()(const Foo* p) const {
return std::hash<int>{}(p->value); // 假设 Foo 有 int value
}
};
struct FooContentEqual {
bool operator()(const Foo* a, const Foo* b) const {
return a && b && a->value == b->value;
}
};
std::unordered_set<Foo*, FooContentHash, FooContentEqual> set;注意:这种写法要求 p 非空,且 value 稳定;否则运行时崩或逻辑错。
跨平台兼容性与性能提醒
std::hash<void></void> 在主流 STL 实现中都是 O(1)、无锁、无分配,但细节有差异:
- libstdc++(GCC):对
std::size_t值做__hash_combine式扰动(类似x ^ (x >> 12)) - libc++(Clang):直接返回
std::size_t值,不做额外处理(更轻,但低地址可能聚集) - MSVC:类似 libstdc++,有简单位移异或
这些差异不影响正确性,但会影响哈希分布质量 —— 如果你用指针地址做大量 unordered_map 键,且地址集中在某段内存(比如栈上连续分配),某些实现可能出现轻微碰撞上升。这不是 bug,是设计取舍。
真正容易被忽略的是:指针哈希值在进程重启后完全不同,也无法跨进程共享;如果做持久化或网络传输,千万别存这个哈希值。










