【问题标题】:Why is "insert" of unordered_set Undefine Behavior here?为什么在这里“插入” unordered_set Undefine Behavior?
【发布时间】:2022-01-16 04:39:04
【问题描述】:

我很抱歉我的标题模糊。

假设有一些节点的指针,我想收集具有唯一value的节点指针。

struct node_t
{
    int value;
    node_t(int v = -1) : value(v) {}
};

例如,如果我们有 4 个指针:

p1 points to node(1)
p2 points to node(1)
p3 points to node(2)
p4 points to node(2)

那我想在这里收集{p1, p3}

这是我的代码写的:

#include <iostream>
#include <unordered_set>
#include <algorithm>
using namespace std;
struct node_t
{
    int value;
    node_t(int v = -1) : value(v) {}
};
struct myequal
{
    bool operator()(const node_t *p1, const node_t *p2) const
    {
        return p1->value == p2->value;
    }
};
int main()
{
    unordered_set<node_t *, hash<node_t *>, myequal> table;
    node_t n1(0), n2(0);
    table.insert(&n1), table.insert(&n2);
    cout << (&n1) << '\n';
    cout << (&n2) << '\n';
    cout << table.size() << '\n';
    cout << *table.begin() << '\n';
}

我在 MacOS12 上运行代码,用clang++ -std=c++17 xxx.cpp 编译它,但它的输出不确定。

有时它会输出:

0x7ff7bad974e0
0x7ff7bad974d0
1
0x7ff7bad974e0

但有时它会输出:

0x7ff7b4bdc4e0
0x7ff7b4bdc4d0
2
0x7ff7b4bdc4d0

为什么会这样?

根据unordered_setdocument

只有当每个元素不等同于容器中已有的任何其他元素时才会插入每个元素(unordered_set 中的元素具有唯一值)。

【问题讨论】:

  • 输出看起来差不多。 &amp;n1n1 的内存地址。你只是在打印那个。输出的前两行是n1n2这两个内存地址。第四行也是一样。 *table.begin() 是一个指向内存地址的指针,您正在那里打印一个指针。
  • 您使用指针作为键,键存储是基于散列的。您正在散列指针本身,而不是它们指向的节点值。但是您正在使用节点值来实现键相等。您有 2 个具有相同值的节点,因此理论上表大小应为 1,但 2 个节点的哈希值不同。我认为不一致导致了您所看到的行为。
  • 等效元素的哈希值必须相等。因此,如果您定义自定义相等,您还必须定义相应的自定义哈希。
  • 我没有在 cplusplus.com(您的文档来源)上看到它,但如果您访问 cppreference.com 的 documentation for unordered set,则有一个指向指定要求 UnorderedAssociativeContainer 的链接。相关信息是第三段(单行,就在“since C++20”框之前)。我希望我只是忽略了 cplusplus.com 上的一些内容,因为这对于编写自己的相等运算符的人来说是一种重要信息。

标签: c++ stl c++17


【解决方案1】:

简短的版本是您的哈希和相等操作不兼容。插入元素时,首先获取哈希,然后检查该哈希的存储桶以查看是否存在等效元素。

假设有三个存储桶,分别命名为 A、B 和 C。您插入 n1,然后它最终进入,比如说,存储桶 B。接下来您插入 n2

  • 如果 n2 被散列到存储桶 B,则在该存储桶中找到等效的 n1,因此不会插入 n2
  • 如果 n2 被散列到存储桶 A(或 C),则检查该存储桶(并且仅检查该存储桶)以查看该元素是否已存在。 Bucket A 是空的,所以当然没有找到等价的元素。所以n2被插入了。

为了使您的哈希和相等操作兼容,相等的事物必须计算为相同的哈希。这确保了相同的东西将被分配给同一个桶(确保n2 在上面的例子中被散列到桶 B)。如果相等基于p1-&gt;value,那么哈希最好基于p1-&gt;value

来自cppreference.com的文档:

如果两个 Keys 根据 Pred 相等,则 Hash 必须为两个键返回相同的值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-19
    • 1970-01-01
    • 2020-08-31
    • 1970-01-01
    • 1970-01-01
    • 2023-03-26
    相关资源
    最近更新 更多