What would the best practice to hold a non owning reference to a object, that can be deleted?
The first part is fairly simple, I simply using the stupid-smart pointer: observer_ptr. However, the last part makes it somewhat more difficult.
Example
Having this setup, to illustrate the need of my vector unique ptr
class Object
{
};
class Derrived : public Object
{
};
With the implementation of
vector<nonstd::observer_ptr<Object>> _observers;
vector<unique_ptr<Object>> _objects;
auto t = make_unique<Derrived>();
_observers.push_back(nonstd::make_observer(t.get()));
_objects.push_back(move(t));
// Same objects
cout << (_observers.at(0).get() == _objects.at(0).get()) << endl;
Issue
Now at any time, somewhere, one of the objects in _objects might be deleted.
I will simply illustrate this by deleting the first object in the vector:
_objects.erase(_objects.begin());
This will result in the _objects vector is empty. However, the _observers vector now points to a freed memory space.
Of course, I can simply delete the observer from _observers, but imagine having such observing references in different parts of my program.
Would there be any cleaner solution for this, and it this the right way to observe different objects?
Please let me know if the example at hand does not illustrate the problem (or any problem for that matter) that I described.
Your use-case sounds like a std::weak_ptr<Object> would be suitable non-owning representation. Of course, for a std::weak_ptr<T> the owning representation is std::shared_ptr<T>. However, since you’ll need to “pin” the object before you could access a std::weak_ptr<T> you’d have more than one owner anyway while accessing the pointer.
As stated in the comments, this is a typical use-case for 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.
Example:
vector<shared_ptr<Object>> objects;
objects.push_back(make_shared<Derived>());
weak_ptr<Object> ptr{ objects.back() };
auto sh_ptr = ptr.lock(); // increase reference count if object is still alive
if(sh_ptr) { // if object was not deleted yet
sh_ptr->doStuff(); // safely access the object, as this thread holds a valid reference
}
Today there is no way to make non-owning relationship to be enforced by compiler:
1. weak_ptr could be converted to shared_ptr
2. Everything else could be deleted.
3. Wrappers around weak_ptr that would be non convertible to shared_ptr would not work also: once reference to an object is retrieved it could be deleted too.
Related
I have implemented my own smart pointers, and everything worked fine, untill I realized a fatal flaw with my implementation. The problem was the fact that an object can have a smart pointer that potentially holds a reference to itself. the problem would be easy to avoid if this was a one layer problem - what could easly happen is a ref counted class would indirectly (through one of its members) holds a referenece to itself. this would mean that a object would never be removed deleted. Is there any way/method I could solve this?
simplest example:
class Derived : public Object {
public:
static ref<Object> Create() { return ref<Object>(new Derived()); }
private:
Derived() : m_ref(this) // m_ref now holds a reference to Derived instance
{
// SOME CODE HERE
}
ref<Object> m_ref;
};
Object is base class contains reference counter, ref is smart pointer that holds a reference to its assigned object
There is no easy way to handle this issue. It is a fundamental problem with reference counting.
To build intuition as to why this is the case, note that the difficulty of detecting cycles of smart pointers is similar to the difficulty of dealing with the cycles. To detect cycles you need to be able to traverse the pointers from "root pointers". If you could do that, you could mark the ones you see during traversal. If you could mark them you could implement mark-and-sweep, which is garbage collection.
In C++11, you can use a shared_ptr<> to establish an ownership relation with an object or variable and weak_ptr<> to safely reference that object in a non-owned way.
You can also use unique_ptr<> to establish an ownership relation with an object or variable. But what if other, non-owning objects want to also reference that object? weak_ptr<> isn't helpful in this case. Raw pointers are helpful but bring various downsides (e.g. they can be automatically initialized to nullptr but this is accomplished through techniques that are not consistent with the std::*_ptr<> types).
What is the equivalent of weak_ptr<> for non-owning references to objects owned via unique_ptr<>?
Here's a clarifying example that resembles something in a game I'm working on.
class World
{
public:
Trebuchet* trebuchet() const { return m_trebuchet.get(); }
private:
std::unique_ptr< Trebuchet > m_trebuchet;
};
class Victim
{
public:
Victim( Trebuchet* theTrebuchet ) : m_trebuchet( theTrebuchet ) {}
~Victim()
{
delete m_trebuchet; // Duh. Oops. Dumb error. Nice if the compiler helped prevent this.
}
private:
Trebuchet* m_trebuchet; // Non-owning.
};
shared_ptr< Victim > createVictim( World& world )
{
return make_shared< Victim >( world.trebuchet() );
}
Here we use a raw pointer to maintain a non-owning relationship with an object owned via unique_ptr<> elsewhere. But is raw the best we can do?
The hope is a type of pointer that:
Looks like the other modern pointer types. E.g. std::raw_ptr<T>.
Replaces raw pointers so that a codebase that uses modern pointer types throughout can find all pointers via a search for _ptr< (roughly).
Auto-initializes to nullptr.
Thus:
int* p; // Unknown value.
std::raw_ptr< int > p; // null.
Does this type already exist in C++ now, is it proposed for the future, or is another implementation broadly available in e.g. Boost?
The "notify" behavior of shared_ptr requires reference counting the reference count control block. shared_ptr's reference count control block(s) use separate reference counts for this. weak_ptr instances maintain references to this block, and weak_ptrs themselves prevent the reference count control block from being deleteed. The pointed-to object has its destructor called when the strong count goes to zero (which may or may not result in deleteion of the memory where that object was stored), and the control block is deleteed only when the weak reference count goes to zero.
unique_ptr's tenet is that it has zero overhead over a plain pointer. Allocating and maintaining reference count control blocks (to support weak_ptr-ish semantics) breaks that tenet. If you need behavior of that description, then you really want shared semantics, even if other references to the object are non-owning. There's still sharing going on in that case -- the sharing of the state of whether or not the object has been destroyed.
If you need a generic nonowning reference and don't need notification, use plain pointers or plain references to the item in the unique_ptr.
EDIT:
In the case of your example, it looks like Victim should ask for a Trebuchet& rather than a Trebuchet*. Then it's clear who owns the object in question.
class World
{
public:
Trebuchet& trebuchet() const { return *m_trebuchet.get(); }
private:
std::unique_ptr< Trebuchet > m_trebuchet;
};
class Victim
{
public:
Victim( Trebuchet& theTrebuchet ) : m_trebuchet( theTrebuchet ) {}
~Victim()
{
delete m_trebuchet; // Compiler error. :)
}
private:
Trebuchet& m_trebuchet; // Non-owning.
};
shared_ptr< Victim > createVictim( World& world )
{
return make_shared< Victim >( world.trebuchet() );
}
There is a genuine need for a standard pointer type to act as a non-owning, inexpensive, and well-behaved counterpoint to std::unique_ptr<>. No such pointer has been standardized yet, but a standard has been proposed and is under discussion by the C++ standards committee. The "World's Dumbest Smart Pointer", aka std::exempt_ptr<> would have the general semantics of other modern C++ pointer classes but would hold no responsibility either for owning the pointed-to object (as shared_ptr and unique_ptr do) or for correctly responding to the deletion of that object (as weak_ptr does).
Assuming that this feature is ultimately ratified by the committee, it would fully meet the need highlighted in this question. Even if it isn't ratified by the committee, the above linked document fully expresses the need and describes a complete solution.
unique_ptr's non-owing analog is a plain C pointer. What is different - C pointer doesn't know if the pointed data is still accessible. weak_ptr on the other hand does. But it is impossible to replace raw pointer with a pointer knowing about the validity of data without additional overhead (and weak_ptr does have that overhead). That implies C-style pointer is the best in terms of speed you can get as a non-owing analog for unique_ptr.
While you can't get a "weak" pointer to a uniquely owned object for free, the concept is useful and is used in a couple systems. See Chromium's WeakPtr and QT's QPointer for implementations.
Chromium's WeakPtr is implemented intrusively by storing a shared_ptr inside the weak-referenceable object and marking it invalid when the object is destroyed. WeakPtrs then reference that ControlBlock and check whether it's valid before handing out their raw pointer. I assume QT's QPointer is implemented similarly. Because ownership isn't shared, the original object is destroyed deterministically.
However, this means that dereferencing the WeakUniquePtr isn't thread-safe:
Thread 1:
unique_ptr<MyObject> obj(new MyObject);
thread2.send(obj->AsWeakPtr());
...
obj.reset(); // A
Thread2:
void receive(WeakUniquePtr<MyObject> weak_obj) {
if (MyObject* obj = weak_obj.get()) {
// B
obj->use();
}
}
If line A happens to run concurrently with line B, thread 2 will wind up using a dangling pointer. std::weak_ptr would prevent this problem by atomically taking a shared owning reference to the object before letting thread 2 use it, but that violates the assumption above that the object is owned uniquely. That means that any use of a WeakUniquePtr needs to be synchronized with the destruction of the real object, and the simplest way to do that is to require that they're done in a message loop on the same thread. (Note that it's still completely safe to copy the WeakUniquePtr back and forth across threads before using it.)
One could imagine using a custom deleter in std::unique_ptr to implement this using standard library types, but that's left as an exercise for the reader.
boost::optional<Trebuchet&>
As Billy ONeal pointed out in his answer you likely want to pass a Trebuchet& instead of a pointer. The problem with the reference is that you cannot pass a nullptr, boost::optional provides a way to have the equivilent of a nullptr. Further details on boost::optional are here: http://www.boost.org/doc/libs/1_54_0/libs/optional/doc/html/boost_optional/detailed_semantics.html
See also this question: boost::optional<T&> vs T*
Note: std::optional<T> is on track to make it into C++14 but std::optional<T&> is a separate proposal that is not in the current C++14 draft. Further details here: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3672.html
In the new C++ world with shared_ptr, weak_ptr, and unique_ptr you should not be storing long lived references to objects, like your trebuchet, using raw pointers or references. Instead World should have a shared_ptr to the trebuchet and Victim should store either a shared_ptr or a weak_ptr, depending on whether the trebuchet should stick around with the victim if the world goes away. Using a weak_ptr allows you to tell if the pointer is still valid (i.e. the world still exists), there is no way to do this with a raw pointer or reference.
When you use a unique_ptr you are declaring that only the World instance will own the trebuchet. Clients of the World class can use the World object's trebuchet by calling the "get" method but should not hold on to the reference or pointer returned by the method when they are done using it. Instead they should "borrow" the trebuchet every time they want to use it by calling the "get" method.
The above being said there could be instances where you want to store a reference or raw pointer for future use to avoid the overhead of the shared_ptr. But those instances are few and far between and you need to be completely sure that you won't use the pointer or reference after the World object that owns the trebuchet has gone away.
otn::raw::weak (from C++ Object Token Library) is non-owning, inexpensive, and well-behaved counterpoint to std::unique_ptr. Also in the library there is otn::safe::unique, a unique owner which can "notify" a non-owning otn::safe::weak about the deletion of the object.
#include <otn/all.hpp>
#include <iostream>
int main()
{
using namespace std;
using namespace otn;
raw::weak_optional<int> raw_weak;
if (!raw_weak)
cout << "raw_weak is empty" << endl;
cout << "--- create object in std_unique..." << endl;
auto std_unique = std::make_unique<int>(42);
raw_weak = std_unique;
if (std_unique)
cout << "std_unique is not empty" << endl;
if (raw_weak)
cout << "raw_weak is not empty" << endl;
cout << "--- move std_unique to safe_unique..." << endl;
safe::unique_optional<int> safe_unique = std::move(std_unique);
if (!std_unique)
cout << "std_unique is empty" << endl;
if (raw_weak)
cout << "raw_weak is not empty, it is observs safe_unique" << endl;
safe::weak_optional<int> safe_weak = safe_unique;
if (safe_unique)
cout << "safe_unique is not empty" << endl;
if (!safe_weak.expired())
cout << "safe_weak is not expired" << endl;
cout << "--- destroy object in safe_unique..." << endl;
utilize(std::move(safe_unique));
if (!safe_unique)
cout << "safe_unique is empty" << endl;
if (safe_weak.expired())
cout << "safe_weak is expired, it is not dangling" << endl;
if (raw_weak)
cout << "raw_weak is not empty, it is dangling!!!" << endl;
}
Output:
raw_weak is empty
--- create object in std_unique...
std_unique is not empty
raw_weak is not empty
--- move std_unique to safe_unique...
std_unique is empty
raw_weak is not empty, it is observs safe_unique
safe_unique is not empty
safe_weak is not expired
--- destroy object in safe_unique...
safe_unique is empty
safe_weak is expired, it is not dangling
raw_weak is not empty, it is dangling!!!
A function taking a raw pointer or reference implicitly promises not to hold on to a copy of that pointer after the function has returned. In return the caller promises that the pointer is valid (or nullptr) until the callee has returned.
If you want to hold on to the pointer, you are sharing it (and should use shared_ptr). A unique_ptr manages a single copy of the pointer. You use raw pointers (or references) to refer to call functions involving that object.
This is the same for shared_ptr objects. weak_ptr only comes into play when you want to have an additional reference to the pointed too object that outlives the involved function. The main purpose of weak_ptr is to break reference cycles where two objects hold references to each other (and are therefore never released).
Remember however that taking shared_ptr or weak_ptr implies that the function taking that parameter will (optionally) modify some other object to retain a reference to the pointed to object that outlives the invocation of the function. In the vast majority of cases you use raw pointer (if nullptr is a valid value) or ref (when a value is guaranteed) even for shared_ptr or weak_ptr.
now you hava noshared_ptr --
https://github.com/xhawk18/noshared_ptr
noshared_ptr<T> -- a new type of unique ptr
noweak_ptr<T> -- weak ptr for the noshared_ptr
In order to make the discussion clear, I'm going to describe the problem in a very general manner, i.e. I will neither provide names of real classes nor will I describe the domain/context (however, I might if it turns out to be urgent).
Imagine class A. Let this class have 2 immutable fields, for instance x and y (please, notice, that these could be potentially big objects, i.e. inefficient to copy). Additionally, let these x and y be primary fields, i.e. only they are used in the implementation of ==/!= operators as well as hash-computing function.
Since A is immutable in terms of x and y, the idea is to let multiple instances of A (say a1 and a2) which have a1.x == a2.x a1.y == a2.y (i.e. a1 == a2) to implicitly have shared access to those x and y, so that there is no unnecessary duplication.
Moreover, now imagine that there is another field in A: z, which is secondary and mutable, and serves as a sort of behavior tweak for A. By design, it is desired to make this field shared among equal instances of A too. So, if I invoke a1.setZ(...) this change will also affect a2 because their access to z is shared.
As a result, we end up with a class A which has pure value semantics, but shares its members implicitly across equal instances. AFAIK such pattern is called Flyweight or aliasing.
One more detail before we move to the question. Most classes in the project are implemented using Pimpl idiom:
private:
class Private;
Private* p;
and class A is not an exclusion. That's why the proposed idea of implementing the scheme described above is as follows.
Use shared pointer to A::Private instead of raw one in
Pimpl idiom;
Have global set of shared pointers to A::Private;
In constructor of A to check whether a shared
pointer to suitable A::Private already exists in the set
(utilizing x and y of course), and if yes, then simply set p
to it, otherwise create new instance of A::Private and store
shared pointer to it in this set, and similarly set p to it;
A::Private's destructor should remove shared pointer to this from the set.
This looks like the most straightforward and intuitive implementation. However, the problem is that since this global set holds a shared pointer to A::Private, it means that even when all instances of corresponding A are destroyed, the reference counter will stay on 1, i.e. it will never reach 0, and thus the memory is never freed.
I thought it would be good if some shared pointers would offer a method to set lower bound for the reference counter. In this case, for example, I would simply set it to 1 which would mean that when it reaches 1 it frees the memory. Unfortunately, I haven't found any implementation of such behavior in popular libraries (Boost, Qt, Poco, etc.). Of course, I could do manual reference counting for my problem, but that just doesn't feel right and smells like reinventing the wheel.
Probably, there are other ways to solve this problem. Looking forward for your suggestions.
NOTE: I would like to immediately intercept any advising to transform the problem to pointer semantics which I am well aware of. I need the solution exactly for the scheme described above.
If I understood correctly what your design issue is, then I would let the global set contain weak, non-owning pointers (e.g. weak_ptr<>) which are able to check if they are dangling, yet they do not increase the reference count.
std::vector<std::weak_ptr<Private>> _objects;
Therefore, when all owning shared pointers to an object are destroyed, the object will be destroyed as well**.
Now your global set will be left with a dangling weak_ptr<>, but the nice thing is that you can check whether that pointer points to an object which is alive or not (use the lock() member function to obtain a possibly null shared_ptr<>. And if it doesn't, you won't dereference it:
// A simple, hypothetical loop through the collection of objects
// which does something, but checks whether the pointers are
// dangling before doing that something on a possibly dead object
// that would be Undefined Behavior)
std::for_each(_objects.begin(), _objecs.end(), [] (std::weak_ptr<Private> p)
{
std::shared_ptr<Private> sp = p.lock();
if (sp != nullptr)
{
sp->callMember(); // For instance...
}
});
If you also want to remove the corresponding weak_ptr<> to an object from the collection once the object gets destroyed, then you could use a custom deleter routine. Your routine will be invoked when the object is destroyed and will be passed the pointer to that object: at this point, before deallocating, you can erase the corresponding element from the set.
For example, a function that instantiates new objects of type A and returns a shared_ptr to it could look this way:
static std::shared_ptr<object> make_A()
{
std::shared_ptr<Private> sp(
new Private(), // Instantiate the object
[] (Private* p) // Set up the custom deleter...
{
// Remove the corresponding element from the vector...
_objects.erase(
// ...so let's find that element!
std::find_if(
_objects.begin(),
_objects.end(),
[p] (std::weak_ptr<priv> wp)
{
// lock() will return a null pointer if wp is dangling
std::shared_ptr<priv> sp = wp.lock();
// In case wp is not dangling, return true if and only
// if it points to the object we're about to delete
return ((sp != nullptr) && (sp.get() == p));
})
);
});
}
Here I assumed C++11, you could easily do the same in C++03 by replacing std::shared_ptr<> with boost::shared_ptr<>, std::weak_ptr<> with boost::weak_ptr<>, and lambdas with properly-defined functors.
Hope this helps.
Have you checked Boost.Flyweight out?
I have some code that currently uses raw pointers, and I want to change to smart pointers. This helps cleanup the code in various ways. Anyway, I have factory methods that return objects and its the caller's responsibility to manager them. Ownership isn't shared and so I figure unique_ptr would be suitable. The objects I return generally all derive from a single base class, Object.
For example,
class Object { ... };
class Number : public Object { ... };
class String : public Object { ... };
std::unique_ptr<Number> State::NewNumber(double value)
{
return std::unique_ptr<Number>(new Number(this, value));
}
std::unique_ptr<String> State::NewString(const char* value)
{
return std::unique_ptr<String>(new String(this, value));
}
The objects returned quite often need to be passed to another function, which operates on objects of type Object (the base class). Without any smart pointers the code is like this.
void Push(const Object* object) { ... } // push simply pushes the value contained by object onto a stack, which makes a copy of the value
Number* number = NewNumber(5);
Push(number);
When converting this code to use unique_ptrs I've run into issues with polymorphism. Initially I decided to simply change the definition of Push to use unique_ptrs too, but this generates compile errors when trying to use derived types. I could allocate objects as the base type, like
std::unique_ptr<Object> number = NewNumber(5);
and pass those to Push - which of course works. However I often need to call methods on the derived type. In the end I decided to make Push operate on a pointer to the object stored by the unique_ptr.
void Push(const Object* object) { ... }
std::unique_ptr<Object> number = NewNumber(5);
Push(number.get());
Now, to the reason for posting. I'm wanting to know if this is the normal way to solve the problem I had? Is it better to have Push operate on the unique_ptr vs the object itself? If so how does one solve the polymorphism issues? I would assume that simply casting the ptrs wouldn't work. Is it common to need to get the underlying pointer from a smart pointer?
Thanks, sorry if the question isn't clear (just let me know).
edit: I think my Push function was a bit ambiguous. It makes a copy of the underlying value and doesn't actually modify, nor store, the input object.
Initially I decided to simply change the definition of Push to use
unique_ptrs too, but this generates compile errors when trying to use
derived types.
You likely did not correctly deal with uniqueness.
void push(std::unique_ptr<int>);
int main() {
std::unique_ptr<int> i;
push(i); // Illegal: tries to copy i.
}
If this compiled, it would trivially break the invariant of unique_ptr, that only one unique_ptr owns an object, because both i and the local argument in push would own that int, so it is illegal. unique_ptr is move only, it's not copyable. It has nothing to do with derived to base conversion, which unique_ptr handles completely correctly.
If push owns the object, then use std::move to move it there. If it doesn't, then use a raw pointer or reference, because that's what you use for a non-owning alias.
Well, if your functions operate on the (pointed to) object itself and don't need its address, neither take any ownership, and, as I guess, always need a valid object (fail when passed a nullptr), why do they take pointers at all?
Do it properly and make them take references:
void Push(const Object& object) { ... }
Then the calling code looks exactly the same for raw and smart pointers:
auto number = NewNumber(5);
Push(*number);
EDIT: But of course no matter if using references or pointers, don't make Push take a std::unique_ptr if it doesn't take ownership of the passed object (which would make it steal the ownership from the passed pointer). Or in general don't use owning pointers when the pointed to object is not to be owned, std::shared_ptr isn't anything different in this regard and is as worse a choice as a std::unique_ptr for Push's parameter if there is no ownership to be taken by Push.
If Push does not take owenrship, it should probably take reference instead of pointer. And most probably a const one. So you'll have
Push(*number);
Now that's obviously only valid if Push isn't going to keep the pointer anywhere past it's return. If it does I suspect you should try to rethink the ownership first.
Here's a polymorphism example using unique pointer:
vector<unique_ptr<ICreature>> creatures;
creatures.emplace_back(new Human);
creatures.emplace_back(new Fish);
unique_ptr<vector<string>> pLog(new vector<string>());
for each (auto& creature in creatures)
{
auto state = creature->Move(*pLog);
}
For legacy reasons I need to use intrusive pointers, as I need the ability to convert raw pointers to smart pointers.
However I noticed there is no weak intrusive pointer for boost. I did find a talk about it on the boost thread list, however nothing concrete.
Does anyone know of a thread safe implementation of weak intrusive pointer?
Thanks
Rich
It does not make any sense.
To elaborate: weak_ptr points to the same instance of a counter object that shared_ptr do. When the shared_ptr goes out of scope, the instance of the counter stays (with a count effectively at 0), which allows the weak_ptr instances to check that they effectively point to a freed object.
With Intrusive Counting, the counter is integrated within the object. When the count reaches 0, the object is usually either recycled or deleted... but the point is the counter is no longer available. The rationale is that this allow for a more efficient storage (1 single chunk) and greater speed (cache locality).
If you need Weak Reference counting and do not care for the benefits of intrusive counting, you can use a combination of shared_ptr and weak_ptr.
The idea is to deassociate the counter from the objects.
class Counted
{
// bla
private:
boost::shared_ptr<int> mCounter;
};
Now you can return weak handles:
class WeakHandle
{
public:
explicit WeakHandle(Counted& c): mCounter(c.mCounter), mObject(&c) {}
bool expired() const { return mCounter.expired(); }
private:
boost::weak_ptr<int> mCounter;
Counted* mObject;
};
Here, we deassociate the lifetime of the counter from the lifetime of the object, so that it will survive the destruction of the object... partially. Thus making the weak_ptr effectively possible.
And of course, using shared_ptr and weak_ptr this is Thread Safe ;)
I didn't really like either of the previous answers so:
No, I don't know of an implementation, but I think it is possible. The standard implementation of the shared_ptr holds two reference counts, one for the "strong" and one for the "weak" references, and a pointer to the referent. In an intrusive_ptr implementation the strong count needs to be part of the object, but the weak can't be. So, it seems like you could create a "weakable" intrusive_ptr.
Define a weak pointer helper:
template<class X>
class intrusive_ptr_weak_helper {
long weak_ref_count;
X *target_instance;
};
Then record that into the object beside the reference count:
struct X {
...
intrusive_ptr_weak_helper *ref_weak_helper;
...
long ref_count;
...
};
When constructing X:
ref_count = 0;
ref_weak_helper = NULL;
The "strong" pointer, intrusive_strong_ptr, is identical to intrusive_ptr, until deletion occurs. When the strong ref count goes to zero (before deletion occurs):
if (ref_weak_helper != NULL) {
if (ref_weak_helper->weak_ref_count == 0)
delete ref_weak_helper;
else
ref_weak_helper->target_instance = NULL;
}
The "weak" version, intrusive_weak_ptr, records the pointer to the weak helper, manipulating that reference count, and accessing the target object via the target_instance pointer. When the weak_ref_count decrements to zero the status of target_instance determines whether the helper is deleted or not.
There are many details missing (concurrency concerns for instance) but this is a mixing of the shared_ptr and the intrusive_ptr. It maintains the basic benefits of the intrusive_ptr (cache optimization, reuse of 3rd party intrusive (strong) ref count, strong and weak pointer stand-ins are pointer sized) while adding extra work mainly in the weak reference path.
Current implementation of intrusive pointer is using reference counter. So deleting object delete also delete the counter, so weak_intrusive_pointer will never know that the object was deleted.
If you need to get weak_ptr from this, you probably search boost::enable_shared_from_this<T>.
OpenSceneGraph and its successor, VulkanSceneGraph, each have comprehensive implementations of intrusive strong pointers and associated weak pointers, named ref_ptr<> and observer_ptr<>, respectively.
I don't know every detail of these systems, but it seems they work using an additional object which is informed when the referent (a descendant of the class Referenced) is deleted. Weak pointers use this third object when an attempt is made to convert them to strong pointers.
VulkanSceneGraph is the next generation scene graph that is currently in development and intended to replace OpenSceneGraph, so I assume its intrusive pointer system is a more advanced implementation.
Worth checking out:
https://github.com/vsg-dev/VulkanSceneGraph/blob/master/include/vsg/core/observer_ptr.h