侯捷 C++ 课程学习笔记:一个万用的 Hash Function

个人简介
在这里插入图片描述
作者简介:全栈研发,具备端到端系统落地能力,专注大模型的压缩部署、多模态理解与 Agent 架构设计。 热爱“结构”与“秩序”,相信复杂系统背后总有简洁可控的可能。
我叫观熵。不是在控熵,就是在观测熵的流动
个人主页:观熵
个人邮箱:privatexxxx@163.com
座右铭:愿科技之光,不止照亮智能,也照亮人心!

专栏导航

观熵系列专栏导航:
AI前沿探索:从大模型进化、多模态交互、AIGC内容生成,到AI在行业中的落地应用,我们将深入剖析最前沿的AI技术,分享实用的开发经验,并探讨AI未来的发展趋势
AI开源框架实战:面向 AI 工程师的大模型框架实战指南,覆盖训练、推理、部署与评估的全链路最佳实践
计算机视觉:聚焦计算机视觉前沿技术,涵盖图像识别、目标检测、自动驾驶、医疗影像等领域的最新进展和应用案例
国产大模型部署实战:持续更新的国产开源大模型部署实战教程,覆盖从 模型选型 → 环境配置 → 本地推理 → API封装 → 高性能部署 → 多模型管理 的完整全流程
TensorFlow 全栈实战:从建模到部署:覆盖模型构建、训练优化、跨平台部署与工程交付,帮助开发者掌握从原型到上线的完整 AI 开发流程
PyTorch 全栈实战专栏: PyTorch 框架的全栈实战应用,涵盖从模型训练、优化、部署到维护的完整流程
深入理解 TensorRT:深入解析 TensorRT 的核心机制与部署实践,助力构建高性能 AI 推理系统
Megatron-LM 实战笔记:聚焦于 Megatron-LM 框架的实战应用,涵盖从预训练、微调到部署的全流程
AI Agent:系统学习并亲手构建一个完整的 AI Agent 系统,从基础理论、算法实战、框架应用,到私有部署、多端集成
DeepSeek 实战与解析:聚焦 DeepSeek 系列模型原理解析与实战应用,涵盖部署、推理、微调与多场景集成,助你高效上手国产大模型
端侧大模型:聚焦大模型在移动设备上的部署与优化,探索端侧智能的实现路径
行业大模型 · 数据全流程指南:大模型预训练数据的设计、采集、清洗与合规治理,聚焦行业场景,从需求定义到数据闭环,帮助您构建专属的智能数据基座
机器人研发全栈进阶指南:从ROS到AI智能控制:机器人系统架构、感知建图、路径规划、控制系统、AI智能决策、系统集成等核心能力模块
人工智能下的网络安全:通过实战案例和系统化方法,帮助开发者和安全工程师识别风险、构建防御机制,确保 AI 系统的稳定与安全
智能 DevOps 工厂:AI 驱动的持续交付实践:构建以 AI 为核心的智能 DevOps 平台,涵盖从 CI/CD 流水线、AIOps、MLOps 到 DevSecOps 的全流程实践。
C++学习笔记?:聚焦于现代 C++ 编程的核心概念与实践,涵盖 STL 源码剖析、内存管理、模板元编程等关键技术
AI × Quant 系统化落地实战:从数据、策略到实盘,打造全栈智能量化交易系统


侯捷 C++ 课程学习笔记:一个万用的 Hash Function


一、引言:Hash 的魅力

在 C++ 标准模板库(STL)中,关联式容器 unordered_mapunordered_set 的核心就是 Hash Table,而 Hash Table 的性能好坏,最终取决于一个最基本的构建模块——Hash Function(哈希函数)

在侯捷老师的课程中,我们不仅学到了 STL 底层的设计哲学,还对 Hash Function 的泛用性有了深入的理解。这篇笔记,我将围绕“一个万用的 Hash Function”展开,探讨它的设计、用途、适用类型,以及如何进行扩展与优化。


二、哈希函数的基本概念回顾

什么是哈希函数?

哈希函数(Hash Function)是将任意类型的数据“压缩”成一个固定范围的整型值的函数。

比如:

size_t hash_value = std::hash<std::string>()("OpenAI");

它的核心目标是:

  • 快速计算
  • 分布均匀
  • 类型泛化

STL 中的 hash 使用方式

#include <unordered_map>
#include <string>

std::unordered_map<std::string, int> map;
map["apple"] = 5;  // 自动调用 std::hash<std::string>

但你是否想过:std::hash 是怎么支持所有类型的?我们能不能设计出一个真正“万用”的哈希函数呢?

侯捷老师在课程中提出了这样一个目标:设计一个可用于任意数据类型的 hash function 模板,并支持用户扩展。


三、std::hash 的限制

std::hash 的局限性

  1. 并非所有 STL 类型都有默认 hash 特化(如 std::pair 就没有)。
  2. 自定义类型不支持,需要你手动写 operator()
  3. 哈希方法不一定适合所有场景。

因此,我们需要设计一个 自定义泛型哈希器,支持如下需求:

  • 支持内置类型(int、double 等)
  • 支持 STL 容器(如 vector、pair、map)
  • 支持用户自定义类型
  • 可组合性强,便于扩展

四、侯捷风格的 Hash 万用模板 —— 设计目标

目标:

  • 泛型设计:使用模板元编程,支持任意类型。
  • 组合性:支持将多个成员变量的 hash 组合为一个。
  • 可特化:对于特殊结构(如 pair、vector)可单独定制。
  • 复用性:可用于 unordered_map 等容器中。

五、实现:一个万用的 Hash Function

我们分为四步实现这套框架:

Step 1:基础结构模板

template<typename T>
struct my_hash {
    size_t operator()(const T& key) const {
        return std::hash<T>{}(key); // 默认转发
    }
};

Step 2:特化基础类型(可省略)

对内置类型不需要做太多事情,因为 std::hash 已支持。

template<>
struct my_hash<int> {
    size_t operator()(int key) const {
        return std::hash<int>()(key);
    }
};

Step 3:组合器 hash_combine

重点来了,我们需要一个组合函数将多个 hash 合并:

inline void hash_combine(std::size_t& seed, std::size_t hash) {
    seed ^= hash + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}

这是 Boost Hash 中的经典做法:使用“黄金比例”常数增强 hash 扩散。


六、支持 pair 类型的 hash

STL 中 std::pair 没有内建 hash,我们可以这样实现:

template<typename T1, typename T2>
struct my_hash<std::pair<T1, T2>> {
    size_t operator()(const std::pair<T1, T2>& p) const {
        size_t seed = 0;
        my_hash<T1> h1;
        my_hash<T2> h2;
        hash_combine(seed, h1(p.first));
        hash_combine(seed, h2(p.second));
        return seed;
    }
};

✅ 示例使用:

#include <unordered_map>
#include <utility>

std::unordered_map<std::pair<int, int>, std::string, my_hash<std::pair<int, int>>> mp;
mp[{1, 2}] = "坐标(1,2)";

七、支持 vector 类型的 hash

template<typename T>
struct my_hash<std::vector<T>> {
    size_t operator()(const std::vector<T>& v) const {
        size_t seed = 0;
        my_hash<T> h;
        for (const auto& item : v) {
            hash_combine(seed, h(item));
        }
        return seed;
    }
};

✅ 示例使用:

std::unordered_map<std::vector<int>, std::string, my_hash<std::vector<int>>> vec_map;
vec_map[{1,2,3}] = "向量123";

八、自定义类型的支持

我们以一个 Point 结构为例:

struct Point {
    int x, y;
};

template<>
struct my_hash<Point> {
    size_t operator()(const Point& p) const {
        size_t seed = 0;
        my_hash<int> h;
        hash_combine(seed, h(p.x));
        hash_combine(seed, h(p.y));
        return seed;
    }
};

✅ 示例使用:

std::unordered_map<Point, std::string, my_hash<Point>> pt_map;
pt_map[{3, 4}] = "这是一个点";

九、hash_combine 的数学原理解析

为什么使用 0x9e3779b9

这是 2^32 的黄金分割常数,近似 2654435769,用于尽可能分散二进制位。

整体结构:

seed ^= hash + 0x9e3779b9 + (seed << 6) + (seed >> 2);
  • seed << 6seed >> 2 提供扰动
  • ^= 避免 hash 碰撞
  • 加法提供扩散

这使得组合哈希更加均匀,冲突概率更小。


十、一个完整可运行的例子

#include <iostream>
#include <unordered_map>
#include <vector>
#include <string>

// hash_combine
inline void hash_combine(std::size_t& seed, std::size_t hash) {
    seed ^= hash + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}

// 泛型模板
template<typename T>
struct my_hash {
    size_t operator()(const T& key) const {
        return std::hash<T>{}(key);
    }
};

// pair 特化
template<typename T1, typename T2>
struct my_hash<std::pair<T1, T2>> {
    size_t operator()(const std::pair<T1, T2>& p) const {
        size_t seed = 0;
        hash_combine(seed, my_hash<T1>()(p.first));
        hash_combine(seed, my_hash<T2>()(p.second));
        return seed;
    }
};

// vector 特化
template<typename T>
struct my_hash<std::vector<T>> {
    size_t operator()(const std::vector<T>& v) const {
        size_t seed = 0;
        for (const auto& item : v)
            hash_combine(seed, my_hash<T>()(item));
        return seed;
    }
};

int main() {
    std::unordered_map<std::pair<int, int>, std::string, my_hash<std::pair<int, int>>> mp;
    mp[{3, 4}] = "点(3,4)";
    
    std::unordered_map<std::vector<int>, std::string, my_hash<std::vector<int>>> vm;
    vm[{1, 2, 3}] = "向量";

    std::cout << mp[{3,4}] << std::endl;
    std::cout << vm[{1,2,3}] << std::endl;

    return 0;
}

十一、和 Boost::hash 的比较

特性Boost::hashmy_hash 实现
可组合性
泛型支持
STL 容器支持✅(需手动特化)
易读性一般非常清晰
可维护性一般(偏宏)高(模板 + 明确结构)

十二、高级拓展:元组支持(tuple)

可以使用 std::apply + fold expression

template<typename... Types>
struct my_hash<std::tuple<Types...>> {
    size_t operator()(const std::tuple<Types...>& t) const {
        size_t seed = 0;
        std::apply([&seed](const auto&... args) {
            (hash_combine(seed, my_hash<std::decay_t<decltype(args)>>()(args)), ...);
        }, t);
        return seed;
    }
};

十三、总结与思考

在学习 STL 与底层实现时,我们不能满足于“直接使用”。要深入掌握 STL 容器的真正威力,必须理解底层机制,特别是 哈希机制

通过构建这个万用哈希函数模板:

  • 我们学习了模板元编程与特化技巧
  • 提升了 STL 容器泛化能力
  • 构建了可组合、可扩展、可重用的 Hash 基础设施

这也正是侯捷老师一再强调的:“STL 不仅要用得爽,更要看得懂,改得动,写得出。


十四、结语:不仅仅是 Hash

在 C++ 世界中,每一个基础工具的深入学习,都会带来更强的掌控力。一个“万用的 hash function”不仅是技术手段,更是一种对系统设计的敬畏。

欢迎你一起来动手,把这个“万用 hash function”运用到你自己的工程中去。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

观熵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值