0

0

如何为c++类正确实现哈希函数? (std::hash特化)

冰火之心

冰火之心

发布时间:2026-01-13 04:39:12

|

801人浏览过

|

来源于php中文网

原创

必须自己写 std::hash 特化:当自定义类用作 std::unordered_set 或 std::unordered_map 的键且非标准类型时,否则编译报错;需在 std 命名空间内全特化,返回 std::size_t,纯函数,满足 a == b ⇒ hash(a) == hash(b)。

如何为c++类正确实现哈希函数? (std::hash特化)

什么时候必须自己写 std::hash 特化?

当你把自定义 C++ 类用作 std::unordered_setstd::unordered_map 的键时,编译器会尝试调用 std::hash::operator()()。如果没特化,就会报错:error: call to implicitly-deleted default constructor of 'std::hash'。这不是“建议”而是硬性要求——只要用了无序容器且类型非标准内置/字符串/指针,就得提供哈希逻辑。

如何安全地特化 std::hash

必须在 std 命名空间内为你的类型全特化 std::hash,且不能改变模板参数数量(即只能是 template struct hash)。常见错误是漏掉 namespace std 或写成偏特化(C++ 不允许对 std::hash 做偏特化)。

关键点:

  • 特化必须定义在全局或你自己的头文件中,且在首次使用前可见(通常放头文件末尾或单独 hash.h
  • 返回类型必须是 std::size_t
  • 不能抛异常、不能有副作用、必须是纯函数(相同输入恒定输出)
  • 强烈建议复用标准组件:用 std::hash 对成员逐个哈希,再用 std::hash_combine 风格混合(C++17 起标准未提供,需手写)
namespace std {
template<>
struct hash {
    size_t operator()(const MyPoint& p) const noexcept {
        size_t h1 = hash{}(p.x);
        size_t h2 = hash{}(p.y);
        // 简单但可用的 combine:避免位移为 0
        return h1 ^ (h2 << 1);
    }
};}

为什么不能直接用 std::hash{}(x) ^ std::hash{}(y)

异或(^)本身不抗碰撞:(1,2)(2,1) 会得到相同哈希值。更糟的是,若多个字段同值(如 (0,0), (1,1)),哈希全撞成 0。实际应引入位移、乘法或标准库推荐的 std::hash{}(val) ^ (std::hash{}(other) 变体。

立即学习C++免费学习笔记(深入)”;

更鲁棒的做法(兼容 C++11+):

Prisms AI
Prisms AI

无代码构建AI应用的平台

下载
inline size_t hash_combine(size_t lhs, size_t rhs) noexcept {
    return lhs ^ (rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2));
}
// 在 operator() 中:
return hash_combine(hash{}(p.x), hash{}(p.y));

注意:不要用 std::hashstd::string 成员直接取 .c_str() 哈希——那哈希的是指针地址,不是内容。

结构体含指针、浮点数或自定义比较逻辑时怎么办?

含裸指针(如 int*)作为键成员极危险:不同对象可能指向相同地址,或同一对象多次插入时指针值变化导致哈希不一致。应避免;若必须,确保指针语义等价于值(如用 std::shared_ptr 并哈希其 get() 地址)。

浮点数要小心 NaN:所有 NaN 的哈希值必须相同(std::hash{} 已处理),但手动比较时仍需显式检查 std::isnan 再决定是否参与哈希。

若类已有 operator==,哈希函数必须满足:若 a == b,则 hash(a) == hash(b)。违反这点会导致 unordered_map 查不到已存在的键——这是最隐蔽也最难调试的问题之一。

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

315

2023.08.02

css中float用法
css中float用法

css中float属性允许元素脱离文档流并沿其父元素边缘排列,用于创建并排列、对齐文本图像、浮动菜单边栏和重叠元素。想了解更多float的相关内容,可以阅读本专题下面的文章。

558

2024.04.28

C++中int、float和double的区别
C++中int、float和double的区别

本专题整合了c++中int和double的区别,阅读专题下面的文章了解更多详细内容。

98

2025.10.23

scripterror怎么解决
scripterror怎么解决

scripterror的解决办法有检查语法、文件路径、检查网络连接、浏览器兼容性、使用try-catch语句、使用开发者工具进行调试、更新浏览器和JavaScript库或寻求专业帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

187

2023.10.18

500error怎么解决
500error怎么解决

500error的解决办法有检查服务器日志、检查代码、检查服务器配置、更新软件版本、重新启动服务、调试代码和寻求帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

271

2023.10.25

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

254

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

206

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1463

2023.10.24

Java 项目构建与依赖管理(Maven / Gradle)
Java 项目构建与依赖管理(Maven / Gradle)

本专题系统讲解 Java 项目构建与依赖管理的完整体系,重点覆盖 Maven 与 Gradle 的核心概念、项目生命周期、依赖冲突解决、多模块项目管理、构建加速与版本发布规范。通过真实项目结构示例,帮助学习者掌握 从零搭建、维护到发布 Java 工程的标准化流程,提升在实际团队开发中的工程能力与协作效率。

10

2026.01.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C# 教程
C# 教程

共94课时 | 6.5万人学习

C 教程
C 教程

共75课时 | 4万人学习

C++教程
C++教程

共115课时 | 12万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号