32

Is there a way to distinguish between an assigned (possibly expired) weak_ptr and a non-assigned one.

weak_ptr<int> w1;
weak_ptr<int> w2 = ...;

I understand the following checks for either non-assignment or expiry, but is there a (cheaper?) check for only non-assignment?

if (!w.lock()) { /* either not assigned or expired */ }
Zuza
  • 2,136
  • 4
  • 20
  • 22

2 Answers2

42

You can use two calls to owner_before to check equality with a default constructed (empty) weak pointer:

template <typename T>
bool is_uninitialized(std::weak_ptr<T> const& weak) {
    using wt = std::weak_ptr<T>;
    return !weak.owner_before(wt{}) && !wt{}.owner_before(weak);
}

This will only return true if w{} "==" weak, where "==" compares owner, and according to en.cppreference.com:

The order is such that two smart pointers compare equivalent only if they are both empty or if they both own the same object, even if the values of the pointers obtained by get() are different (e.g. because they point at different subobjects within the same object).

Since the default constructor constructs an empty weak pointer, this can only return true if weak is also empty. This will not return true if weak has expired.

Looking at the generated assembly (with optimization), this seems pretty optimized:

bool is_uninitialized<int>(std::weak_ptr<int> const&):
        cmp     QWORD PTR [rdi+8], 0
        sete    al
        ret

... compared to checking weak.expired():

bool check_expired(std::weak_ptr<int> const&):
        mov     rdx, QWORD PTR [rdi+8]
        mov     eax, 1
        test    rdx, rdx
        je      .L41
        mov     eax, DWORD PTR [rdx+8]
        test    eax, eax
        sete    al
.L41:
        rep ret

... or returning !weak.lock() (~80 lines of assembly).

Dev Null
  • 4,731
  • 1
  • 30
  • 46
Holt
  • 36,600
  • 7
  • 92
  • 139
9

Using std::weak_ptr::expired()

#include <iostream>
#include <memory>

//declare a weak pointer
std::weak_ptr<int> gw;

void f()
{
    //check if expired
    if (!gw.expired()) {
        std::cout << "pointer is valid\n";
    }
    else {
        std::cout << "pointer  is expired\n";
    }
}

int main()
{
    f();
    {
        auto cre = std::make_shared<int>(89);
        gw = cre;
        f();
    } 

    f();
}

Output

pointer  is expired
pointer is valid
pointer  is expired
Program ended with exit code: 0
Hariom Singh
  • 3,512
  • 6
  • 28
  • 52
  • `stl::expired` isn't a thing. You mean [`std::weak_ptr::expired()`](http://en.cppreference.com/w/cpp/memory/weak_ptr/expired). – François Andrieux Aug 04 '17 at 13:03
  • My belief is that this is slower than checking for nullptr in the event the object is assigned. – Zuza Aug 04 '17 at 13:04
  • this response is misleading, the ask is to distinguish between a non initialized and an expired weak_ptr, here is just a test for expiration that does not let discriminating – 8znr Apr 28 '23 at 08:27