Find a value in an unordered_set of shared_ptr
Asked Answered
F

2

5

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?

Formal answered 16/9, 2015 at 15:50 Comment(3)
You need to understand what it means for two shared pointers to be "equal".Monosaccharide
You should not specialize std::hash not for your type...Enrobe
Comparison operators for shared_ptr compare pointer values; the actual objects pointed to are not compared.Delinquent
S
11

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());
}
Saltworks answered 16/9, 2015 at 15:53 Comment(0)
K
1

According to here:

Note that the comparison operators for shared_ptr simply compare pointer values; the actual objects pointed to are not compared.

So found will be true only if shared_ptr points to same object:

typedef std::shared_ptr<int> IntPtr;

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

bool found = s.find(p) != s.end();
cout<<std::boolalpha<<found<<endl; // true
Kreiner answered 16/9, 2015 at 16:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.