C++笔记:std::hash

cpp 复制代码
#include<functional>
template< class Key >
struct hash; 

std::hash 本质上是一个 模板函数对象(function object)

可以把它理解为一个函数对象,自然它实现了operator()方法。

本文不深究具体原理,只讲一下如何使用std::hash

如何使用

cpp 复制代码
#include <iostream>
#include <functional>

int main()
{
    std::hash<int> hasher;

    size_t h1 = hasher(10);
    size_t h2 = hasher(20);

    std::cout << h1 << std::endl;
    std::cout << h2 << std::endl;
}
bash 复制代码
10155668643424727455
14764355265743743681

返回值类型是std::size_t

标准库的规则

  1. 接收 Key 类型的单个参数
  2. 返回表示参数散列值的 std::size_t 类型。
  3. 调用时不抛出异常。
  4. 对于二个相等的参数 k1k2 , std::hash<Key>()(k1) == std::hash<Key>()(k2) 。
  5. 对于二个相异而不相等的参数 k1k2 , std::hash<Key>()(k1) == std::hash<Key>()(k2) 的概率应非常小,接近 1.0/std::numeric_limits<std::size_t>::max() 。

标准库已经提供了很多类型的哈希实现:

bash 复制代码
//基本类型
int
long
float
double
bool
//指针
T*
//字符串
std::string
std::wstring
std::string_view    //C++20支持
//注意对于C字符串const char *,并不会对所有字符进行哈希,而是仅视为指针

std::hash 在 unordered_*里的作用

无序关联容器 std::unordered_set 、 std::unordered_multiset 、 std::unordered_map 、 std::unordered_multimap 以该模板 std::hash 的特化为默认散列函数。

cpp 复制代码
template <class _Kty, class _Ty, class _Hasher = std::hash<_Kty>, class _Keyeq = std::equal_to<_Kty>
class unordered_map;

如何给自定义的类定义hash?

假设我们自定义了一个类Point

cpp 复制代码
struct Point
{
    int x;
    int y;
};
std::unordered_map<Point, int> mp; // ❌ 编译错误

因为std::hash<Point>不存在

哈希表必须知道:Point -> hash

方法1:特化 std::hash(最常用)

cpp 复制代码
#include <iostream>
#include <functional>
#include<unordered_map>
struct Point {
    int x;
    int y;
};
// std::hash 的自定义特化能注入 namespace std
namespace std
{
    template<>
    struct hash<Point>
    {
        size_t operator()(const Point& p) const
        {
            return std::hash<int>{}(p.x) ^ std::hash<int>{}(p.y);
        }
    };
}
int main()
{
    std::hash<Point> hasher;//可以
    std::unordered_map<Point, int> mp;//可以实例化
    mp.insert(std::make_pair(Point{ 1,2 }, 1));//但插入有问题
    
}

哈希表不仅要 hash,还要判断相等。

cpp 复制代码
#include <iostream>
#include <functional>
#include<unordered_map>
struct Point
{
    int x;
    int y;
    //要加入==操作
    bool operator==(const Point& other) const
    {
        return x == other.x && y == other.y;
    }
};
// std::hash 的自定义特化能注入 namespace std
namespace std
{
    template<>
    struct hash<Point>
    {
        size_t operator()(const Point& p) const
        {
            return std::hash<int>{}(p.x) ^ std::hash<int>{}(p.y);
        }
    };
}
int main()
{
    std::hash<Point> hasher;
    std::unordered_map<Point, int> mp;
    mp.insert(std::make_pair(Point{ 1,2 }, 1));
    
}

方法2:自定义 hash 函数(不用特化)

cpp 复制代码
#include <iostream>
#include <functional>
#include<unordered_map>
struct Point
{
    int x;
    int y;

    bool operator==(const Point& other) const
    {
        return x == other.x && y == other.y;
    }
};
//相当于自己设计hash算法的可调用对象类
struct PointHash
{
    std::size_t operator()(const Point& p) const
    {
        return p.x * 31 + p.y;
    }
};
int main()
{
    //让哈希表用自己的算法
    std::unordered_map<Point, int,PointHash> mp;
    mp.insert(std::make_pair(Point{ 1,2 }, 1));
    
}
相关推荐
用户805533698031 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
RainCity1 天前
Java Swing 自定义组件库分享(十二)
java·笔记·后端
BadBadBad__AK2 天前
线段树维护区间 k 次方和
c++·数学·算法·stl
卷无止境2 天前
Eigen 库如何借助 OpenMP 加速计算
c++·后端
卷无止境2 天前
OpenMPI、MPICH 与 OpenMP:关系、核心概念与架构全解
c++·后端
郝学胜_神的一滴3 天前
CMake 30:循环语法全解|foreach_while双循环精讲、迭代技巧与实战避坑指南
c++·cmake
卷无止境5 天前
C++ 的Eigen 库全解析
c++
卷无止境5 天前
现代 C++特性大盘点:一门脱胎换骨的老语言
c++·后端
郝学胜_神的一滴5 天前
CMake 27:缓存变量的特性、语法、类型与实操全解
c++·cmake
博客18007 天前
酷宝的使用方法,超好用的免费界面库,C++、MFC可用
c++·mfc·界面库·库来帮·酷宝