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.
Make your own augmented weak ptr.
It stores a hash value, and supports ==
based off owner_before()
.
You must make these from shared_ptr
s, 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.