Reputation: 14052
While I do understand why there is no operator==
for shared_ptr
and unique_ptr
, I wonder why there is none for shared_ptr
and weak_ptr
. Especially since you can create a weak_ptr
via a reference on shared_ptr
.
I would assume that for 99% of the time you want lhs.get() == rhs.get()
. I would now go forward and introduce that into my code unless someone can name me a good reason, why one should not do such a thing.
Upvotes: 13
Views: 4838
Reputation: 1582
As the other answers have pointed out, simply comparing the underlying pointers would be perilous. For one, consider the following scenario: a weak reference A exists to an object, which is subsequently deleted, and therefore the weak reference expires. Then, another object is allocated in the memory freed up by said deletion, which has the same address. Now the underlying pointers are the same, even though the weak pointer originally referred to a different object!
As the other answers have suggested, one way is to compare shared == weak.lock()
. Since lock()
will return nullptr
(and not some bogus pointer) if the weak pointer expired, his works for identifying if they are equal (as long as shared != nullptr
). However, there are two problems with this:
shared == nullptr
. This can be dangerous in cases where the comparison must remain stable, such as when using it as a key in an unordered_map
or unordered_set
.lock()
is a relatively expensive operation.Fortunately, there is a better way to do this. Both weak_ptr
and shared_ptr
also store a pointer to what is known as the control block, which is what stores the reference counts and outlives the original object for as long as references remain to it. To check whether they refer to the same object, all we need to do is compare the control block pointers. This can be done with the owner_before
method:
template<class T>
bool owner_equals(std::shared_ptr<T> &lhs, std::weak_ptr<T> &rhs) {
return !lhs.owner_before(rhs) && !rhs.owner_before(lhs);
}
This approach will even work for comparing two std::weak_ptr
s with each other, if you wish to know whether they (once) referred to the same object, since the control block will last (at least) as long as all of the weak references.
Do keep in mind that this may not produce the expected result if you are using the aliasing feature of std::shared_ptr
, which is a feature that lets you create two std::shared_ptr
instances with the same control block that nonetheless store different pointers.
Upvotes: 4
Reputation: 791869
weak_ptr
doesn' have a get()
method because you need to explicitly lock the weak_ptr
before you can access the underlying pointer. Making this explicit is a deliberate design decision. If the conversion were implicit it would be very easy to write code that would be unsafe if the last shared_ptr
to the object were to be destroyed while the underlying pointer obtained from the weak_ptr
was still being examined.
This boost page has a good description of the pitfalls and why weak_ptr
has such a limited interface.
If you need to do a quick comparison, then you can do shared == weak.lock()
. If the comparison is true then you know that weak
must still be valid as you hold a separate shared_ptr
to the same object. There is no such guarantee if the comparison returns false.
Upvotes: 19
Reputation: 299860
Because it has a cost.
A weak_ptr
is like an observer, not a real pointer. To do any work with it you first need to obtain a shared_ptr
from it using its lock()
method.
This has the effect of acquiring ownership, but it as costly as copying a regular shared_ptr
(count increment, etc...) so it is nothing trivial.
As such, by not providing ==
, you are forced to step back and actually check whether you really need this or not.
Upvotes: 4