更改了现有的 unordered_set 元素,这里的机制是什么?

问题描述

我在自己的 A 类上创建了一个 unordered_set

dump s:5,7,

程序打印

a7

正如预期的那样。

虽然 a7_s 是不同的指针,但它们不能同时插入集合 A_eq,因为 A_hashs 正在处理解除引用的指针而不是指针本身。

然后我选择了存储在 findfound->v = 7 中的指针,使用 auto iter = s.find(a5_); A* found = *iter; printf("%d\n",found->v); found->v = 7; printf("dump s:"); for (auto& obj : s) { printf("%d,",obj->v); } printf("\n"); s.erase(a7_); s.erase(a7); s.rehash(0); printf("dump s:"); for (auto& obj : s) { printf("%d,obj->v); } printf("\n"); iter = s.find(a7_); printf(iter==s.end()?"no 7":"has 7"); 更改其取消引用的值,以便该集合包含两个“相同”元素。但是集合坚持它包含两个不同的元素。

5
dump s:7,dump s:7,no 7

这些代码输出

A({.v=7})

谁能告诉我发生了什么?为什么剩下的元素不被集合视为chunks

解决方法

[unord.req]/5 ... 对于同一个容器中的任意两个键 k1k2,调用 pred(k1,k2) 应始终返回相同的值。对于容器中的任何键 k,调用 hash(k) 应始终返回相同的值。

您的程序通过违反此要求而表现出未定义的行为。它以一种改变其散列的方式修改一个键,这使得两个键在它们之前比较不相等的地方比较相等。

,

来自 cppreference.comunordered_set 文档:

不能修改容器元素(即使是非 const 迭代器),因为修改可能会改变元素的哈希值并破坏容器。

更准确的说,不只是容器中直接的数据是不允许修改的,而是任何影响这些元素相等比较的数据。虽然您没有修改容器内的位,但您确实通过将容器的元素从不等于更改为等于来在逻辑上修改了它们。因此,您更改了元素的哈希值并损坏了容器。垃圾随之而来。

,

好的,这里的一些用户已经批评了你的黑客计划,完全正确。

但是这里给你一个进一步的提示:

如果您想将自定义类与标准容器一起使用,请尝试使用您的类作为容器的直接类型(无指针/包装器),如果您想提供引用您的实际类内容的自定义比较/散列函数或者至少确保底层存储的数据是常量!

也许这里有一些理论上的少数例外情况,但总的来说,自定义比较器/哈希方法应该始终引用它们包含的“腐朽”类类型,至少对于它们的输入参数传递。否则,您至少会破坏单一职责原则(std-container 已经关心可能的原始指针元素的使用(或者您的类应该使用自己的指针类型),您的类不应再次在这里进行干扰)。