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 */ }
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).
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
You could try to create a shared pointer that accepts a weak pointer as a parameter and raises a std::bad_weak_ptr exception if a weak pointer is expired (or not assigned as in your case):
#include <memory>
#include <iostream>
int main(){
// with an assigned pointer
std::shared_ptr<int> p1(new int(42));
std::weak_ptr<int> w1(p1);
try {
std::shared_ptr<int> p2(w1);
}
catch (const std::bad_weak_ptr& e) {
std::cout << e.what() << '\n';
}
// with a non assigned pointer
std::shared_ptr<int> p2(new int(42));
std::weak_ptr<int> w2;
try {
std::shared_ptr<int> p2(w2); // raises an exception
}
catch (const std::bad_weak_ptr& e) {
std::cout << "Ptr 2: " << e.what() << '\n';
}
}