C++ 更改了现有无序的集合元素,什么';这里的机制是什么?
我在自己的A级上创建了一个无序的_集 结构A{ INTV; }; 结构A_eq { 布尔运算符()(A*lhs,A*rhs)常量{ 返回lhs->v==rhs->v; } }; 结构A_散列 { std::size_t运算符()(A*obj)常量{ 返回std::hash{}(obj->v); } }; int main(int,char*[]){ A*a7=新A();A*a5=新A();A*a7=新A();A*a5=新A(); a7->v=7;a5->v=5;a7->v=7;a5->v=5; std::无序的_集; s、 插入(a7);s.插入(a5);s.插入(a7); printf(“转储s:”); 用于(自动和车载:s){ printf(“%d”,obj->v); } } 程序打印C++ 更改了现有无序的集合元素,什么';这里的机制是什么?,c++,stl,unordered-set,C++,Stl,Unordered Set,我在自己的A级上创建了一个无序的_集 结构A{ INTV; }; 结构A_eq { 布尔运算符()(A*lhs,A*rhs)常量{ 返回lhs->v==rhs->v; } }; 结构A_散列 { std::size_t运算符()(A*obj)常量{ 返回std::hash{}(obj->v); } }; int main(int,char*[]){ A*a7=新A();A*a5=新A();A*a7=新A();A*a5=新A(); a7->v=7;a5->v=5;a7->v=7;a5->v=5;
dump s:5,7,
正如所料
虽然a7
和a7
是不同的指针,但它们不能同时插入到集合s
,因为A_eq
和A_hash
正在处理解引用指针,而不是指针本身
然后,我通过find
拾取存储在s
中的指针,使用find->v=7
更改其解引用值,使集合包含两个“相同”元素。但布景坚持认为它包含两种不同的元素
auto-iter=s.find(a5);
A*found=*iter;
printf(“%d\n”,找到->v);
发现->v=7;
printf(“转储s:”);
用于(自动和车载:s){
printf(“%d”,obj->v);
}
printf(“\n”);
s、 擦除(a7_);
s、 擦除(a7);
s、 再灰分(0);
printf(“转储s:”);
用于(自动和车载:s){
printf(“%d”,obj->v);
}
printf(“\n”);
iter=s.find(a7);
printf(iter==s.end()?“no 7”:“has 7”);
这些代码输出
5
dump s:7,7,
dump s:7,
no 7
有人能告诉我发生了什么事吗?为什么剩余的元素不被集合视为A({.v=7})
[unord.req]/5。。。对于同一容器中的任意两个键k1
和k2
,调用pred(k1,k2)
应始终返回相同的值。对于容器中的任何键k
,调用hash(k)
将始终返回相同的值
您的程序通过违反此要求而表现出未定义的行为。它修改一个键的方式改变了它的散列,使两个键在之前比较不相等的地方比较相等。无序集的文档来自: 不能修改容器元素(即使是非常量迭代器),因为修改可能会更改元素的哈希并损坏容器
更准确地说,不允许修改的不仅仅是容器中直接的数据,还有影响这些元素的相等性比较的任何数据。虽然您没有修改容器内的位,但是您确实通过将容器的元素从不相等更改为相等来在逻辑上修改它们。因此,您更改了元素的哈希并损坏了容器。垃圾接踵而至。好的,这里的几个用户已经完全正确地批评了你的黑客计划 但在这里给您留下一个关于未来的进一步提示: 如果要将自定义类与标准容器一起使用,请尝试将类用作容器的直接类型(无指针/包装器),如果要提供引用实际类内容的自定义比较/哈希函数,或至少确保底层存储的数据为常量
这里可能有一些理论上的少数例外情况,但一般来说,自定义比较器/哈希方法应该总是引用它们包含的“衰减”类类型,至少对于它们的输入参数传递是这样。否则,您至少违反了单一责任原则(std容器已经关心了可能的原始指针元素用法(或者您的类应该有自己的指针类型),您的类不应该在这里再次干涉)。祝贺您,您故意打破了
无序集
不变量,结果一切都坏了。它在插入时通过散列存储内容,因此它基于原始值进行存储,并且查找(使用新值)的位置不正确。unordered_set
使用散列代码标识其内部数据结构中放置元素或查找元素的位置。如果更改元素的方式改变了集合希望存储元素的位置,则会遇到麻烦。这就是为什么你不应该修改一个元素,改变它的哈希代码/与其他元素的相等性。很好的详细答案!这是一个经常被遗忘的细节。常量迭代器是这里的第一个警告标志和安全屏障,但是语言不能也不应该关注所有可能的黑客路径。“这里可能有一些理论上的少数例外情况”——确实有,比如避免。