Search code examples
c++unordered-maphash-function

unordered_map with self-defined hash function cannot work


I want to use type shared_ptr as the key of an unordered_map. In order to use the content of the string as key rather than use the pointer address, I define function "hashFn" as hash function. However, code below cannot work. I insert (p1, 0) and then find p2 which has the same content with p1 in the map, but failed. The code print "0".

#include <iostream>
#include <memory>
#include <unordered_map>
using namespace std;

int main() {
  auto hashFn = [](const shared_ptr<string> &p) -> size_t {
    cout << hash<string>()(*p) << endl;
    return hash<string>()(*p);
  };
  auto map =
      unordered_map<shared_ptr<string>, int, decltype(hashFn)>(0, hashFn);
  auto p1 = shared_ptr<string>(new string("abc"));
  auto p2 = shared_ptr<string>(new string("abc"));
  auto [_it, flag] = map.emplace(p1, 0);
  cout << flag << endl;
  int c = map.count(p2);
  cout << c << endl;
  return 0;
}

I print the hash result in my hashFn and it shows the result of p1 and p2 is the same. It is confusing...

  auto hashFn = [](const shared_ptr<string> &p) -> size_t {
    cout << hash<string>()(*p) << endl;
    return hash<string>()(*p);
  };

Solution

  • p1 and p2 are different pointers, so it is natural that p1 isn't found when searching for p2.

    Different values can have same hash, so equality of hash doesn't mean equality of values.

    If you want to override how to check if keys are equal, you should specify the equal argument in the constructor of std::unordered_map.

    #include <iostream>
    #include <memory>
    #include <unordered_map>
    using namespace std;
    
    int main() {
      auto hashFn = [](const shared_ptr<string> &p) -> size_t {
        cout << hash<string>()(*p) << endl;
        return hash<string>()(*p);
      };
      auto equalFn = [](const shared_ptr<string> &lhs, const shared_ptr<string> &rhs) -> bool {
        return *lhs == *rhs;
      };
      auto map =
          unordered_map<shared_ptr<string>, int, decltype(hashFn), decltype(equalFn)>(0, hashFn, equalFn);
      auto p1 = shared_ptr<string>(new string("abc"));
      auto p2 = shared_ptr<string>(new string("abc"));
      auto [_it, flag] = map.emplace(p1, 0);
      cout << flag << endl;
      int c = map.count(p2);
      cout << c << endl;
      return 0;
    }