Search code examples
c++stlunordered-set

Changed existing unordered_set element, what's the mechanism here?


I created an unordered_set on my own class A

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);
    }
}

the program prints

dump s:5,7,

as expected.

Although a7 and a7_ are different pointers, they cannot both insert into set s, due to the fact that A_eq and A_hash are working on dereferenced pointer instead of pointer itself.

Then I picked the pointer stored in s through find, changed its dereferenced value using found->v = 7, so that the set contained two "same" element. But the set insists it's containing two different element.

    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");

these codes outputs

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

Can someone tell me what's happening? Why the remaining element is not considered as A({.v=7}) by the set?


Solution

  • [unord.req]/5 ... For any two keys k1 and k2 in the same container, calling pred(k1, k2) shall always return the same value. For any key k in a container, calling hash(k) shall always return the same value.

    Your program exhibits undefined behavior by way of violating this requirement. It modifies a key in a way that changes its hash, and that makes two keys compare equal where they compared unequal before.