Namespaces
Variants

std:: hash

From cppreference.net
Utilities library
定义于头文件 <bitset>
定义于头文件 <coroutine>
(C++20 起)
定义于头文件 <chrono>
(C++26 起)
定义于头文件 <filesystem>
(C++17 起)
定义于头文件 <functional>
定义于头文件 <memory>
定义于头文件 <optional>
(C++17 起)
定义于头文件 <stacktrace>
(C++23 起)
定义于头文件 <string>
定义于头文件 <string_view>
(C++17 起)
定义于头文件 <system_error>
定义于头文件 <text_encoding>
(C++26 起)
定义于头文件 <thread>
定义于头文件 <typeindex>
定义于头文件 <utility>
(C++26 起)
定义于头文件 <variant>
(C++17 起)
定义于头文件 <vector>
template < class Key >
struct hash ;
(C++11 起)

hash 模板的启用特化定义了一个实现 哈希函数 的函数对象。

给定类型 Key ,每个特化 std::hash<Key> 要么处于 启用 状态,要么处于 禁用 状态:

  • 如果程序或用户未提供 std::hash<Key> ,则该功能被禁用。
  • 否则,当满足以下所有条件时, std::hash<Key> 将被启用:
  • 满足以下所有要求:
  • 给定以下值:
  • h ,类型为 std::hash<Key> 的对象。
  • k1 k2 ,类型为 Key 的对象。
满足以下所有要求:
  • k1 == k2 true ,则 h ( k1 ) == h ( k2 ) 也应为 true
  • 除非 std::hash<Key> 程序定义的特化 ,否则 h ( k1 ) 绝不会抛出异常。
  • 否则, std::hash<Key> 将被禁用。

被禁用的特化不满足 Hash 要求,不满足 FunctionObject 要求,且下列值均为 false

(注:根据要求,所有HTML标签、属性及 标签内的C++代码均保持原样未翻译,仅对列表项前的描述性文字进行了翻译。由于原文中除代码外无其他可翻译文本内容,故仅添加了说明性注释。)

换言之,它们确实存在,但无法使用。

目录

嵌套类型

类型 定义
argument_type (C++17 起已弃用) Key
result_type (C++17 起已弃用) std::size_t
(C++20 前)

成员函数

构造哈希函数对象
(公开成员函数)
计算参数的哈希值
(公开成员函数)

标准库特化

每个声明 std::hash 模板的头文件同时为以下类型提供已启用的 std::hash 特化:

一个 独立实现 必须提供上述特化以及默认禁用的特化。

(since C++20)

除此之外,某些头文件还为库类型提供了其他启用的 std::hash 特化(参见 下方 )。

对于标准库提供的所有 std::hash 特化版本,除以下情况外,其所有成员函数均为 noexcept

(since C++26)
(since C++17)

标准库类型的特化

语言支持库
std::coroutine_handle 的哈希支持
(类模板特化)
诊断库
std::error_code 的哈希支持
(类模板特化)
std::error_condition 的哈希支持
(类模板特化)
std::type_index 的哈希支持
(类模板特化)
std::stacktrace_entry 的哈希支持
(类模板特化)
std::basic_stacktrace 的哈希支持
(类模板特化)
内存管理库
std::unique_ptr 的哈希支持
(类模板特化)
std::shared_ptr 的哈希支持
(类模板特化)
std::indirect 的哈希支持
(类模板特化)
通用工具库
std::optional 的哈希支持
(类模板特化)
std::variant 的哈希支持
(类模板特化)
std::monostate 的哈希支持
(类模板特化)
std::bitset 的哈希支持
(类模板特化)
容器库
std::vector<bool> 的哈希支持
(类模板特化)
字符串库
字符串的哈希支持
(类模板特化)
时间库
std::chrono::duration 的哈希支持
(类模板特化)
std::chrono::time_point 的哈希支持
(类模板特化)
std::chrono::day 的哈希支持
(类模板特化)
std::chrono::month 的哈希支持
(类模板特化)
std::chrono::year 的哈希支持
(类模板特化)
std::chrono::weekday 的哈希支持
(类模板特化)
std::chrono::weekday_indexed 的哈希支持
(类模板特化)
std::chrono::weekday_last 的哈希支持
(类模板特化)
std::chrono::month_day 的哈希支持
(类模板特化)
std::chrono::month_day_last 的哈希支持
(类模板特化)
std::chrono::month_weekday 的哈希支持
(类模板特化)
std::chrono::month_weekday_last 的哈希支持
(类模板特化)
std::chrono::year_month 的哈希支持
(类模板特化)
std::chrono::year_month_day 的哈希支持
(类模板特化)
std::chrono::year_month_day_last 的哈希支持
(类模板特化)
std::chrono::year_month_weekday 的哈希支持
(类模板特化)
std::chrono::year_month_weekday_last 的哈希支持
(类模板特化)
std::chrono::zoned_time 的哈希支持
(类模板特化)
std::chrono::leap_second 的哈希支持
(类模板特化)
输入/输出库
std::filesystem::path 的哈希支持
(类模板特化)
并发支持库
std::thread::id 的哈希支持
(类模板特化)

注释

实际的哈希函数取决于具体实现,除上述规定要求外无需满足其他质量标准。值得注意的是,某些实现使用将整数映射自身的简易(恒等)哈希函数。换言之,这些哈希函数的设计用途是配合无序关联容器工作,而非作为加密哈希等场景使用。

哈希函数仅需保证在同一程序执行过程中对相同输入产生相同结果;这允许使用加盐哈希来防范碰撞拒绝服务攻击。

没有针对C字符串的特化。 std :: hash < const char * > 生成的是指针值(内存地址)的哈希,不会检查字符数组的内容。

针对 std::pair 和标准容器类型的额外特化版本,以及用于组合哈希的实用函数,可在 boost::hash 中获得。

示例

#include <cstddef>
#include <functional>
#include <iomanip>
#include <iostream>
#include <string>
#include <unordered_set>
struct S
{
    std::string first_name;
    std::string last_name;
    bool operator==(const S&) const = default; // 自 C++20 起
};
// C++20 之前。
// bool operator==(const S& lhs, const S& rhs)
// {
//     return lhs.first_name == rhs.first_name && lhs.last_name == rhs.last_name;
// }
// 自定义哈希可以是独立的函数对象。
struct MyHash
{
    std::size_t operator()(const S& s) const noexcept
    {
        std::size_t h1 = std::hash<std::string>{}(s.first_name);
        std::size_t h2 = std::hash<std::string>{}(s.last_name);
        return h1 ^ (h2 << 1); // 或使用 boost::hash_combine
    }
};
// std::hash 的自定义特化可以注入到 std 命名空间中。
template<>
struct std::hash<S>
{
    std::size_t operator()(const S& s) const noexcept
    {
        std::size_t h1 = std::hash<std::string>{}(s.first_name);
        std::size_t h2 = std::hash<std::string>{}(s.last_name);
        return h1 ^ (h2 << 1); // 或使用 boost::hash_combine
    }
};
int main()
{
    std::string str = "Meet the new boss...";
    std::size_t str_hash = std::hash<std::string>{}(str);
    std::cout << "hash(" << std::quoted(str) << ") =\t" << str_hash << '\n';
    S obj = {"Hubert", "Farnsworth"};
    // 使用独立的函数对象。
    std::cout << "hash(" << std::quoted(obj.first_name) << ", "
              << std::quoted(obj.last_name) << ") =\t"
              << MyHash{}(obj) << " (使用 MyHash) 或\n\t\t\t\t"
              << std::hash<S>{}(obj) << " (使用注入的特化)\n";
    // 自定义哈希使得可以在无序容器中使用自定义类型。
    // 此示例将使用上面注入的 std::hash<S> 特化,
    // 若要改用 MyHash,请将其作为第二个模板参数传递。
    std::unordered_set<S> names = {obj, {"Bender", "Rodriguez"}, {"Turanga", "Leela"}};
    for (auto const& s: names)
        std::cout << std::quoted(s.first_name) << ' '
                  << std::quoted(s.last_name) << '\n';
}

可能的输出:

hash("Meet the new boss...") =  10656026664466977650
hash("Hubert", "Farnsworth") =  12922914235676820612 (using MyHash) or
                                12922914235676820612 (using injected specialization)
"Bender" "Rodriguez"
"Turanga" "Leela"
"Hubert" "Farnsworth"

缺陷报告

下列行为变更缺陷报告被追溯应用于先前发布的 C++ 标准。

缺陷报告 适用版本 发布时行为 正确行为
LWG 2119 C++11 缺少对扩展整数类型的特化 已提供
LWG 2148 C++11 缺少对枚举类型的特化 已提供
LWG 2543 C++11 std::hash 可能不支持 SFINAE 已支持 SFINAE
LWG 2817 C++11 缺少对 std::nullptr_t 的特化 已提供