Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using C++ shared pointer's aliasing constructor with an empty shared pointer

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
    }
like image 968
Григорий Шуренков Avatar asked Sep 02 '17 09:09

Григорий Шуренков


1 Answers

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.

like image 53
alain Avatar answered Oct 06 '22 10:10

alain