Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

`weak_ptr::expired` behavior in the dtor of the object

Consider the following code:

#include <iostream>
#include <memory>
using namespace std;

class T;

std::weak_ptr<T> wptr;

class T
{
public:
    T() {  }
    ~T() {
        std::cout << "in dtor" << std::endl;
        std::cout << (wptr.expired() ? "expired" : "not expired") << std::endl;
    }
};

int main() {
    {
        auto ptr = std::make_shared<T>();
        wptr = ptr;
        std::cout << (wptr.expired() ? "expired" : "not expired") << std::endl;
    }
    return 0;
}

In this code, I was trying to find out if weak_ptrs are expired in the objects destruction phase. It seems so. The output is:

not expired
in dtor
expired

I used gcc-5.1 with ideone.

Now, I have another problem. I couldn't find any documentation stating that this is the standard behavior. Is it guaranteed to work this way, always?

like image 227
jnbrq -Canberk Sönmez Avatar asked Jan 25 '17 12:01

jnbrq -Canberk Sönmez


People also ask

What is std :: Weak_ptr?

std::weak_ptr is a smart pointer that holds a non-owning ("weak") reference to an object that is managed by std::shared_ptr. It must be converted to std::shared_ptr in order to access the referenced object.

What does weak_ ptr lock do?

weak_ptr::lockCreates a new std::shared_ptr that shares ownership of the managed object. If there is no managed object, i.e. *this is empty, then the returned shared_ptr also is empty.

How is Weak_ptr implemented?

To implement weak_ptr , the "counter" object stores two different counters: The "use count" is the number of shared_ptr instances pointing to the object. The "weak count" is the number of weak_ptr instances pointing to the object, plus one if the "use count" is still > 0.


2 Answers

Now, I have another problem. I couldn't find any documentation stating that this is the standard behavior. Is it guaranteed to work this way, always?

No. Indeed, it's underspecified in the standard, as raised by LWG issue 2751.

The C++14 standard contains no language that guarantees the deleter run by a shared_ptr will see all associated weak_ptr instances as expired. For example, the standard doesn't appear to guarantee that the assertion in the following snippet won't fire:

std::weak_ptr<Foo> weak;
std::shared_ptr<Foo> strong{
  new Foo,
  [&weak] (Foo* f) {
    assert(weak.expired());
    delete f;
  },
};

weak = strong;
strong.reset();

It seems clear that the intent is that associated weak_ptrs are expired, because otherwise shared_ptr deleters could resurrect a reference to an object that is being deleted.

Suggested fix: 23.11.3.2 [util.smartptr.shared.dest] should specify that the decrease in use_count() caused by the destructor is sequenced before the call to the deleter or the call to delete p.

The current wording for ~shared_ptr() as linked above simply states that the deleter is invoked, with a non-normative note that the number of instances that share ownership is decreased.

While the intent is probably that weak.expired() when the deleter is called, it's questionable to rely on this. It's really only reasonable to state with confidence that the shared_ptr no longer shares ownership after it's been destroyed - asking that question during destruction is a bit odd.

like image 128
Barry Avatar answered Sep 20 '22 07:09

Barry


Using make_shared like that will create an object with the default constructor you provided.

template< class T, class... Args >
shared_ptr<T> make_shared( Args&&... args );

Constructs an object of type T and wraps it in a std::shared_ptr using args as the parameter list for the constructor of T. The object is constructed as if by the expression (std::make_shared)

After the anonymous scope in the main. The shared ptr will be deleted.

The object is destroyed and its memory deallocated when either of the following happens:

the last remaining shared_ptr owning the object is destroyed; (std::shared_ptr)

.

The destructor of shared_ptr decrements the number of shared owners of the control block. If that counter reaches zero, the control block calls the destructor of the managed object. The control block does not deallocate itself until the std::weak_ptr counter reaches zero as well. std::shared_ptr Implementation notes

This means that your object will call its destructor after the destruction of the last shared ptr has started. The output:

not expired
in dtor
expired

is the expected behavior.

like image 34
Petar Velev Avatar answered Sep 21 '22 07:09

Petar Velev