In the following code, I have defined template arguments Hash and KeyEqual for unordered_map. I expect the output to be 1 1 1 1 but it is actually 1 1 0 1. Why does this happen? Is it because std::equal_to<Base*> is not used in comparing maps with ==?
#include <iostream>
#include <unordered_map>
using std::cout;
using std::endl;
class Base {
public:
int x;
Base(int _x) : x(_x) {}
bool operator==(const Base& another) const {
return x == another.x;
}
size_t hash() const {return x;}
};
template <>
struct std::hash<Base> {
size_t operator()(const Base& r) const {
return r.hash();
}
};
template <>
struct std::hash<Base*> {
size_t operator()(const Base *r) const {
return r->hash();
}
};
template <>
struct std::equal_to<Base*> {
bool operator()(const Base *r1, const Base *r2) const {
return (*r1) == (*r2);
}
};
int main(int, char**){
Base b1(1);
Base b2(2);
Base bb1(1);
Base bb2(2);
cout << (b1 == bb1) << endl;
std::unordered_map<Base, int> map1;
map1.emplace(b1, 1);
map1.emplace(b2, 2);
std::unordered_map<Base, int> map2;
map2.emplace(bb1, 1);
map2.emplace(bb2, 2);
cout << (map1 == map2) << endl;
std::unordered_map<Base*, int, std::hash<Base*>, std::equal_to<Base*>> map11;
map11.emplace(&b1, 1);
map11.emplace(&b2, 2);
std::unordered_map<Base*, int, std::hash<Base*>, std::equal_to<Base*>> map22;
map22.emplace(&bb1, 1);
map22.emplace(&bb2, 2);
cout << (map11 == map22) << endl;
std::unordered_map<Base*, int, std::hash<Base*>, std::equal_to<Base*>> map33;
map33.emplace(&b1, 1);
map33.emplace(&b2, 2);
cout << (map11 == map33) << endl;
}
operator==bypasses theyKeyEqualforstd::unordered_mapContrary to intuition, the
==operator forstd::unordered_mapdoes not care aboutstd::hashor aboutstd::key_equal; it relies on the built-in==operator for your type.- [unord.req.general] p242
Notice how the ranges which are tested for equality (in your case, they will simply contain one pointer each) do not use they
KeyEqualprovided to the container. It is defined in terms ofis_permutationwith noKeyEqual, which simply uses the built-in==operator.What is the rationale for this?!
Containers in general don't consider the
KeyEqual,Less(in the case ofstd::set), etc. which you provide to them. All comparison operators are simply forwarded to the contained elements, and this design is consistent, even though it's counter-intuitive.For two
std::unordered_maps with two (stateful)KeyEquals, there is another motivation:- N2986: Equality Comparison for Unordered Containers
See also Can not compare std::unorded_set with custom KeyEqual