TL;DR How can a std::shared_ptr
be created from an expired std::weak_ptr
so that it uses the same control block as the std::weak_ptr
?
I ran the following test case, and noticed two oddities:
#include <memory>
#include <cstdio>
#define TEST(a, b) printf("%14s has %9s control block as %s\n", #a, \
a.owner_before(b) || b.owner_before(a) ? "different" : "same", #b)
int main() {
std::weak_ptr<int> empty { };
std::weak_ptr<int> expired { std::make_shared<int>() };
std::shared_ptr<int> locked_expired{ expired.lock() };
std::shared_ptr<int> null_nonempty { static_cast<int*>(nullptr) };
std::shared_ptr<int> null_empty { nullptr };
TEST(locked_expired, expired);
TEST(locked_expired, empty );
TEST(null_nonempty, empty );
TEST(null_empty, empty );
}
Outputs:
locked_expired has different control block as expired
locked_expired has same control block as empty
null_nonempty has different control block as empty
null_empty has same control block as empty
There are two oddities:
expired.lock()
creates a std::shared_ptr
which has a different control block than expired
, and which has the same (no) control block as an empty std::weak_ptr
/ std::shared_ptr
.
null_empty
constructed from nullptr
has the same (no) control block as an empty default-constructed std::shared_ptr
/ std::weak_ptr
, while null_nonempty
constructed from static_cast<int*>(nullptr)
has a different control block than an empty std::weak_ptr
.
This is different from what I would have expected:
I would expect that it should create a std::shared_ptr
with the same control block as expired
, but of course with a null pointer stored in it (.get() == nullptr
) since it was expired.
I would expect that a std::shared_ptr
constructed from a nullptr
to behave the same as a std::shared_ptr
constructed from static_cast<element_type*>(nullptr)
and create a std::shared_ptr
with a new control block, different from a default-constructed std::shared_ptr
with no control block.
I am not concerned with #2, since it is easily worked around. But #1 seems intractable.
How can a std::shared_ptr
be created from an expired std::weak_ptr
so that it shares the same control block as the std::weak_ptr
, ie expired.lock()
=> std::shared_ptr
using same control block and ownership as expired
but with a null pointer stored in it?
I am interested in preserving ownership relationships between arbitrary std::shared_ptr
and std::weak_ptr
instances, and am more concerned about the control blocks than about the managed storage.
The owner_less
relation is specified so that any two empty smart pointers compare equivalent. This constrains the behavior that the result of lock()
on an expired weak_ptr
must be equivalent to all other default-constructed shared_ptr
s.
Additionally, the design of shared_ptr
excludes the possibility of being attached to an expired control block. Changing this would eliminate an important invariant and force expiration checks on all shared_ptr
operations which currently are not needed, slowing down all users.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With