std::shared_ptr has an aliasing constructor that allows newly created shared_ptr to share state with an existing shared pointer while pointing to some other object.
I was thinking about abusing this constructor to put pointer to some global object inside shared_ptr:
int global = 0;
int main()
{
// because we point to global object we do not need to track its lifetime
// so we use empty shared_ptr<void> as a provider of shared state
std::shared_ptr<int> p(std::shared_ptr<void>(), &global);
std::shared_ptr<int> pp = p;
return *pp;
}
My question is: Is it legal? The code successfully works on major compilers.
Note, that I do not ask if it's a good thing to do. I do understand that there's a canonical way of putting pointers to global objects into shared_ptr using no-op deleter. It is also a bit disturbing if it is legal, because it would be possible to have dereferenceable shared_ptr, weak pointers to which are always expired:
std::shared_ptr<int> p(std::shared_ptr<void>(), &global);
std::weak_ptr<int> w = p;
if (p) // p is alive and well
{ // and w is not
*w.lock(); // and here program crashes
}
As you already know, with your current solution, p
has a use_count()
of zero, that's why the weak_ptr
is expired. This seems to be ok, according to the C++ draft N4296:
20.8.2.2.1 shared_ptr constructors [util.smartptr.shared.const]
template shared_ptr(const shared_ptr& r, T* p) noexcept;
13 Effects: Constructs a shared_ptr instance that stores p and shares ownership with r.
14 Postconditions: get() == p && use_count() == r.use_count()
15 [ Note: To avoid the possibility of a dangling pointer, the user of this constructor must ensure that p remains valid at least until the ownership group of r is destroyed. — end note ]
16 [ Note: This constructor allows creation of an empty shared_ptr instance with a non-null stored pointer. — end note ]20.8.2.2.2 shared_ptr destructor [util.smartptr.shared.dest]
~shared_ptr();
1 Effects:
(1.1) — If *this is empty or shares ownership with another shared_ptr instance (use_count() > 1), there are no side effects.
(1.2) — Otherwise, if *this owns an object p and a deleter d, d(p) is called.
(1.3) — Otherwise, *this owns a pointer p, and delete p is called
emphasis mine.
You could use the following instead which gives a shared_ptr
with a use_count()
of one:
std::shared_ptr<int> p(&global, [](int*){});
This uses an empty custom deleter.
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