Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When is a std::weak_ptr empty? Is an expired std::weak_ptr empty?

According to [util.smartptr.weak.obs]/4, the weak_ptr::owner_before strict weak ordering is such that "two shared_­ptr or weak_­ptr instances are equivalent if and only if they share ownership or are both empty."

However, I see nothing in the standard that defines what it means for a weak_ptr to be empty. Obviously a default-constructed weak_ptr is empty, and a weak_ptr constructed from an empty shared_ptr is empty, but it doesn't seem to be explicitly stated whether an expired weak_ptr is empty.

like image 864
Brian Bi Avatar asked Oct 23 '25 17:10

Brian Bi


2 Answers

While I don't see it guaranteed by the Standard, usability1 of the owner_before ordering requires that:

  • A weak_ptr which has become expired2 is NOT EMPTY.

This is because only mutation of an object should change its placement in an ordering, and a weak_ptr may expire without ever being mutated.


1 For example, here someone has used weak_ptr as a key in std::set: How to compute hash of std::weak_ptr?. Doing that, and std::map likewise, requires the ordering to survive expiration. Were there to be an owner_hash to allow use with std::unordered_set and std::unordered_map, that hash would also have to survive expiration.


2 Note the subtle difference between "has become expired" and "is expired" -- a weak_ptr constructed empty, or assigned with an empty pointer value, has expired() == true. But it didn't expire (action verb), it was created in an expired state. My answer only applies to weak_ptr values which are at some point not expired, then (passively) become expired due to detachment of shared_ptr values from the target.

like image 164
Ben Voigt Avatar answered Oct 26 '25 05:10

Ben Voigt


A shared_ptr is empty when it does not own a pointer, which means it does not refer to a control block. This is true, because a control block is where the owned pointer is stored, as well as the strong and weak reference counts. So for a shared_ptr being non-empty means participating in ownership of a control block. And conversely, being empty means not owning any control block.

Equivalently, a weak_ptr is empty when it does not own a control block.

For a shared_ptr, being empty also implies use_count() == 0, because if it owned a control block then the use count (i.e. the strong reference count) would be at least 1, because the use count tells you how many shared_ptr objects own the control block.

But a weak_ptr can own a control block which has no more shared_ptr owners, so the use count is 0. That means that weak_ptr::expired() can be true for a non-empty weak_ptr. Because the weak_ptr owns the control block, the weak reference count must be at least 1, but there is no accessor for that weak ref count, so you can't observe it. So now we can answer this part of the question:

it doesn't seem to be explicitly stated whether an expired weak_ptr is empty.

An empty weak_ptr is expired (by definition, because use_count() returns 0 if it's empty), but an expired weak_ptr is not necessarily empty. It might own a control block which has no more shared_ptr owners, but still has one or more weak_ptr owners.

like image 38
Jonathan Wakely Avatar answered Oct 26 '25 05:10

Jonathan Wakely