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.