Search code examples
c++unordered-map

When using pair<int, int> as the key of unordered_map, do I need to provide a custom hash function?


can i declare an unordered_map with pair<int, int> as key without providing a custom hash function, as long as the compiler and standard library support C++11 or later.? just like this:

#include <iostream>
#include <unordered_map>
#include <utility>

int main() {
    // Declare an unordered_map with pair<int, int> as key and int as value
    std::unordered_map<std::pair<int, int>, int> myMap;

    // Example usage:
    myMap[{1, 2}] = 10;
    myMap[{3, 4}] = 20;

    // Accessing values
    int value1 = myMap[{1, 2}];
    int value2 = myMap[{3, 4}];

    return 0;
}

as i try it on my macbook with clion, it always suggeste errors like this:

error: static_assert failed due to requirement 'integral_constant<bool, false>::value' "the specified hash does not meet the Hash requirements" static_assert(__check_hash_requirements<_Key, _Hash>::value,

However chatGPT always say it can work. i'm confused.


Solution

  • Instead of ChatGPT, use a proper reference such as https://en.cppreference.com/w/cpp/utility/hash. That page lists all the specializations provided by the standard library, and you can see clearly std::pair is not there, so your code cannot possibly work. That page even contains a note saying

    Note: additional specializations for std::pair and the standard container types, as well as utility functions to compose hashes are available in boost::hash

    There is at least one important reason for not providing a default implementation for a std::pair: we can hardly give a suitable hash function without knowing what the two members mean and how they interact. In fact, many people have emphasized that std::pair should be avoided except in templated code because it does not carry any semantic information about its members and if the members happen to be the same type, or one is convertible to the other, it is quite easy to just swap them accidentally. In most cases, using a structure with properly named fields is a better choice. Standard library functions also tend to avoid using a std::pair in such cases. For example, for unary transforms, std::ranges::transform returns a std::ranges::in_out_result, which is a properly named structure with two iterator fields also named properly, instead of returning a std::pair of iterators.

    Also note that specializing std::hash<T> for T=std::pair<int,int> is undefined behavior. If for some reason you want to stick to using std::pair after all that have been said, and want to provide a hash function, you need to use the third template parameter (i.e. Hash) for std::unordered_map, instead of specializing std::hash.