c++hashweak-ptr

How to compute hash of std::weak_ptr?


So I have code that uses std::weak_ptr and maintains them in an std::set, and that works just fine -- and has worked for the last 5 or 7 years. Recently I thought I'd fiddle with using them in an std::unordered_set (well, actually in an f14::F14ValueSet) and for that, I would need a hash of it. As of now, there is no std::hash<std::weak_ptr>, so what should I do instead?

The answer seems to be "just hash the control block", as implied by this question and reply: Why was std::hash not defined for std::weak_ptr in C++0x?, but how do I get access to the control block? In glibc, it's located at __weak_ptr<>::_M_refcount._M_pi-> but that's private (and implementation specific). What else can I do?

One answer is "just wait": maybe someday there will be a standard owner_hash() for std::weak_ptr, but I'd prefer something available now.


Solution

  • Make your own augmented weak ptr.

    It stores a hash value, and supports == based off owner_before().

    You must make these from shared_ptrs, as a weak ptr with no strong references cannot be hashed to match its owner; this could create two augmented weak ptrs that compare equal but hash differently.

    template<class T>
    struct my_weak_ptr {
      // weak ptr API clone goes here.  lock() etc.
    
      // different ctor:
      my_weak_ptr(std::shared_ptr<T>const& sp){
        if(!sp) return;
        ptr=sp;
        hash = std::hash<T*>{}(sp.get());
      }
      std::size_t getHash()const{return hash;}
      friend bool operator<(my_weak_ptr const& lhs, my_weak_ptr const& rhs){
        return lhs.owner_before(rhs);
      }
      friend bool operator!=(my_weak_ptr const& lhs, my_weak_ptr const& rhs){
        return lhs<rhs || rhs<lhs;
      }
      friend bool operator==(my_weak_ptr const& lhs, my_weak_ptr const& rhs){
        return !(lhs!=rhs);
      }
    private:
      std::weak_ptr<T> ptr;
      std::size_t hash=0;
    };
    

    these have stable, sensible hashes. While a recycled object pointer results in a hash collision, so long as they don't share control blocks they won't be equal.

    namespace std{template<class T>struct hash<some_ns::my_weak_ptr<T>>{
      std::size_t operator()(my_weak_ptr<T> const& wp)const{return wp.getHash();}
    };}
    

    One warning: Use of aliasing constructor could result in pathological results. As equality is based on control block equality, not pointer value.