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?
[unord.req]/5 ... For any two keys
k1
andk2
in the same container, callingpred(k1, k2)
shall always return the same value. For any keyk
in a container, callinghash(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.