【问题标题】:std::hash vs std::equal_to instantiation without required functions没有必需功能的 std::hash 与 std::equal_to 实例化
【发布时间】:2014-05-28 19:18:39
【问题描述】:

我仍在学习如何使用标准库容器std::mapstd::unordered_map。我最近尝试提供一个 hash 函数或 comparison 函数。

有两点我不明白:

std::map<int, int, equal_to<int>> myMap1; // Works
std::map<int, int, hash<int>> myMap2; // Works

std::unordered_map<int, int, equal_to<int>> myMap3; // Doesn't work
std::unordered_map<int, int, hash<int>> myMap4; // Works

为什么我可以为std::map 提供哈希函数,而我却不能为std::unordered_map 提供比较函数?

此外,如果我为比较函数 std::equal_to 使用自定义结构而不重载 == 运算符,则不会出现错误。为什么会这样?

【问题讨论】:

    标签: c++ map std unordered-map


    【解决方案1】:

    标准 C++ 库中有一个东西叫做“概念”。概念是一组要求。通过使用std::unordered_map(或任何遵循某个概念的内容,就此而言),您同意您将遵守其规定的要求。

    通过使用

    std::map<int, int, equal_to<int>> myMap1;
    std::map<int, int, hash<int>> myMap2;
    std::unordered_map<int, int, equal_to<int>> myMap3;
    

    您违反了std::mapstd::unordered_map 的要求 - 编译器不需要检查您的错误,myMap1myMap2myMap3 肯定无法正常工作。

    恰好Hash 要求它返回std::size_t 类型的值。这很容易检查。

    这与 Compare 的状态要求不同,后者表示无法轻松检查(例如,std::size_t 可转换为 bool) - 因此,它的使用很可能在运行时失败。

    此外,您似乎对哈希函数和比较函数的用途感到困惑——它们是不同的。

    哈希返回一个 std::size_t 类型的值,对于任何 a, b 类型的 T

    Hash(a) != Hash(b) implies a != b
    and
    a == b implies Hash(a) == Hash(b)
    

    就是这样——不多也不少。它应该比比较函数更快,因此您可以轻松比较值 - 如果散列不同,则值肯定不同,如果散列相同,则进行完整比较。

    【讨论】:

    • 顺便说一下,如果我有一个包含 40 个整数的巨大结构,我如何将它们组合成一个 size_t?不会有重复吗?
    • 哦,没关系。我想我现在明白了。哈希表以某种方式将具有相同 size_t 的键重新组合到桶中(或类似的东西?)。
    • @user2924010:通常,您对每个 int 进行哈希处理,并使用 hash_combine 将它们组合成一个哈希值。如果两个结构具有相同的哈希值是可以的,但它发生的越少,容器的工作速度就越快。
    • 嗯,我很迷茫。在 [link](www.cplusplus.com) 上,这是 size_t 的描述:它是一种能够以字节表示任何对象大小的类型: size_t 是 sizeof 运算符返回的类型,在标准中被广泛使用库来表示大小和计数。
    • @user2924010 你不能简单地生成“随机”数字(另外,定义“随机”是什么意思),因为如果你只是生成“随机”数字,Hash(5) == Hash(5) 不会不再是真的(根据a == b implies Hash(a) == Hash(b) 的要求)
    【解决方案2】:

    因为这两种结构非常不同:

    • 哈希函数对地图没有意义。地图需要一个可选的比较函数(它是一个有序结构),您的前两个示例没有传递比较函数。默认为更少。

    具体来说是

    二元谓词,以两个元素键作为参数,返回 如果第一个参数在第二个参数之前,则为 true 它定义了严格的弱排序,否则为假。这将是一个 函数指针或函数对象。成员类型 key_compare 是 容器使用的内部比较对象类型,在 map 中定义 作为其第三个模板参数 (Compare) 的别名。如果 key_compare 使用默认的less(没有状态),这个参数不是 相关的。

    • unordered_map 基本上是一个哈希表(它没有有序状态)并且需要一个哈希函数,但您没有向它传递一个哈希函数。

    比较函数对象,如果两个容器返回true 作为参数传递的对象键被认为是相等的。成员 类型 key_equal 在 unordered_map 中定义为其第四个的别名 模板参数(Pred)。

    来自http://www.cplusplus.com的报价

    【讨论】:

    • 那我该如何定义一个哈希函数呢?我不知道怎么做。
    • 对于自定义哈希函数,已经在 SO 上进行了广泛的讨论,例如看这里:stackoverflow.com/questions/15869066/…
    • 问题是它对我来说真的很复杂。我在 stackoverflow 上找到了很多关于哈希函数的帖子,但从来没有像我这样的初学者的基本教程。例如,两个不同的变量能否为哈希函数返回相同的 size_t?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-31
    • 1970-01-01
    • 1970-01-01
    • 2014-01-08
    • 1970-01-01
    • 2019-07-09
    相关资源
    最近更新 更多