I want to compare two std::weak_ptr's or one std::weak_ptr and one std::shared_ptr for equality.
What I want to know is whether the object each of the weak_ptr's/shared_ptr's point to is the same. The comparison should yield negative results not just if the addresses don't match, but also if the underlying object was deleted and then reconstructed with the same address by chance.
So basically, I want this assertion to hold even if the allocator reserves the same address:
auto s1 = std::make_shared<int>(43);
std::weak_ptr<int> w1(s1);
s1.reset();
auto s2 = std::make_shared<int>(41);
std::weak_ptr<int> w2(s2);
assert(!equals(w1,w2));
The weak_ptr templates do not provide equality operators, and as I understood that's for a good reason.
So a naive implementation would look like this:
template <typename T, typename U>
inline bool naive_equals(const std::weak_ptr<T>& t, const std::weak_ptr<U>& u)
{
return !t.expired() && t.lock() == u.lock();
}
template <typename T, typename U>
inline bool naive_equals(const std::weak_ptr<T>& t, const std::shared_ptr<U>& u)
{
return !t.expired() && t.lock() == u;
}
If the first weak_ptr expired in the meantime, it yields 0. If not, I upgrade the weak_ptr to a shared_ptr and compare the addresses.
The problem with this is that i have to lock the weak_ptr's twice (once)! I'm afraid that takes too much time.
I came up with this:
template <typename T, typename U>
inline bool equals(const std::weak_ptr<T>& t, const std::weak_ptr<U>& u)
{
return !t.owner_before(u) && !u.owner_before(t);
}
template <typename T, typename U>
inline bool equals(const std::weak_ptr<T>& t, const std::shared_ptr<U>& u)
{
return !t.owner_before(u) && !u.owner_before(t);
}
Which checks if the owner block of u is not "before" t's and t's not before u's, so t == u.
Does this work as I intend it? Do two weak_ptr's created from distinct shared_ptr's always compare as non-equal this way? Or did I miss something?
Edit: Why do I want to do this in the first place? I want to have a container with shared pointers, and I want to hand out references to the objects in it. I can't use iterators, since they may be invalidated. I could hand out (integer) ID's, but that leads to problems with uniqueness and would require a map type and complicate search/insertion/removal operations. The idea is to use a std::set and give out the pointers themselves (capsuled in a wrapper class) as keys, so that clients can use the weak_ptr's to access the objects in the set.
See Question&Answers more detail:os