提问者:小点点

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


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

struct A {
    int v;
};

struct A_eq
{
    bool operator()(A* lhs, A* rhs) const {
        return lhs->v == rhs->v;
    }
};

struct A_hash
{
    std::size_t operator()(A* obj) const {
        return std::hash<int>{}(obj->v);
    }
};

int main(int , char* []) {
    A* a7 = new A(); A* a5 = new A(); A* a7_ = new A(); A* a5_ = new A();
    a7->v = 7; a5->v = 5; a7_->v = 7; a5_->v = 5;

    std::unordered_set<A*, A_hash, A_eq> s;
    s.insert(a7); s.insert(a5); s.insert(a7_);

    printf("dump s:");
    for (auto& obj : s) {
        printf("%d,", obj->v);
    }
}

程序打印

dump s:5,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,7,
dump s:7,
no 7

有人能告诉我发生了什么事吗?为什么剩余的元素不被集合视为


共2个答案

匿名用户

[UNORD.REQ/5..对于同一容器中的任意两个键,调用应始终返回相同的值。对于容器中的任何键,调用将始终返回相同的值。

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

匿名用户

来自CPPreference.com的的文档:

容器元素不能被修改(即使是通过非常量迭代器),因为修改可能会改变元素的哈希值并损坏容器。

更确切地说,不允许修改的不仅仅是容器中直接存在的数据,还有任何影响这些元素的相等性比较的数据。虽然您没有修改容器内的位,但您确实在逻辑上修改了容器的元素,将它们从不相等更改为相等。因此,您更改了元素的哈希值并损坏了容器。垃圾接踵而至。