Using Unordered Data Structures on C++ std::pair
In many situations, it seems fairly natural to use std::unordered_set
and
std::unordered_map
on std::pair
. Here’s an example of what you might
be tempted to do:
#include <unordered_set>
int main(void) {
std::unordered_set<std::pair<int, int>> test;
}
However, std::pair
is not hashable by default, so a simple snippet like the
above would not work.
There are many proposals online to define a pairhash
class and
explicitly specify it as the hash function as a template parameter to
std::unordered_set
and std::unordered_map
.
This is not a bad idea. In fact, if you are writing a library, you should probably do this. But we can do better…
We can actually use a quick fix to make the exact snippet above work:
namespace std {
template<typename X, typename Y>
struct hash<std::pair<X, Y>> {
std::size_t operator()(const std::pair<X, Y> &pair) const {
return std::hash<X>()(pair.first) ^ std::hash<Y>()(pair.second);
}
};
}
You can put this snippet before any instatiation of std::unordered_set
or std::unordered_map
with a std::pair
parameter as the hash key, and
the code would work as you expect. For example:
#include <unordered_set>
namespace std {
template<typename X, typename Y>
struct hash<std::pair<X, Y>> {
std::size_t operator()(const std::pair<X, Y> &pair) const {
return std::hash<X>()(pair.first) ^ std::hash<Y>()(pair.second);
}
};
}
int main(void) {
std::unordered_set<std::pair<int, int>> test;
}
This lets you write fairly sane and clean code. However, you should avoid
doing this in a reusable library since it might conflict with someone else’s
code, if they also pull a similar trick to define a hash function for
std::pair
. It would also stop working if a new C++ standard introduces
hashing for std::pair
out of the box.
But, I hope you find this snippet useful, perhaps internally, or in single use applications, like programming contests or assignments.