c++shared-ptrunordered-set

Find a value in an unordered_set of shared_ptr


I'd like to find a value in unordered_set, but failed:

typedef std::shared_ptr<int> IntPtr;

std::unordered_set<IntPtr> s;
s.insert(std::make_shared<int>(42));

bool found = s.find(std::make_shared<int>(42)) != s.end();
cout<<std::boolalpha<<found<<endl; // false

Had tried following but still not working.

namespace std {
  template <> struct hash<IntPtr> {
    size_t operator()(const IntPtr& x) const noexcept {
      return std::hash<int>()(*x);
    }
  };
}

Any idea how to make it works?


Solution

  • You stored a pointer to an integer. When you look up items in the set, you're not comparing the (pointed-to) integer, but the pointer itself.

    When you allocate a new pointer to a new integer object for the search, it won't compare equal, because it's a different integer object (even though it stores the same value).

    Your options are:

    1. don't store pointers to integers in your set, just store the integers directly.

      Then, your key is 42, and searching for 42 will find it, because the integers are compared by value

    2. store pointers and use a custom hash and comparator to compare the pointed-at integers instead of the pointers.

      You shouldn't (try to) pollute std namespace with your hash specialization, and it's not sufficient anyway (the hash is used for bucket lookup, but keys are still compared with KeyEqual inside the bucket). Just specify them for your container.

    Example code for #2:

    #include <cassert>
    #include <memory>
    #include <unordered_set>
    
    namespace Deref {
        struct Hash {
            template <typename T>
            std::size_t operator() (std::shared_ptr<T> const &p) const {
                return std::hash<T>()(*p);
            }
        };
        struct Compare {
            template <typename T>
            size_t operator() (std::shared_ptr<T> const &a,
                               std::shared_ptr<T> const &b) const {
                return *a == *b;
            }
        };
    }
        
    int main() {
        std::unordered_set<std::shared_ptr<int>> sp;
        auto p = std::make_shared<int>(42);
        sp.insert(p);
        assert(sp.find(p) != sp.end()); // same pointer works
        assert(sp.find(std::make_shared<int>(42)) == sp.end()); // same value doesn't
    
        // with the correct hash & key comparison, both work
        std::unordered_set<std::shared_ptr<int>, Deref::Hash, Deref::Compare> spd;
        spd.insert(p);
        assert(spd.find(p) != spd.end());
        assert(spd.find(std::make_shared<int>(42)) != spd.end());
    }