I was reading this article by Herb Sutter about passing smart pointers to functions. He doesn't mention std::weak_ptr
and honestly I can't find a good scenario where passing such smart pointer is useful.
Does the function take ownership? Pass std::shared_ptr
. Does the function just need to operate on the underlying object? Pass a raw pointer or a reference.
So is passing std::weak_ptr
to functions 100% useless?
By using a weak_ptr , you can create a shared_ptr that joins to an existing set of related instances, but only if the underlying memory resource is still valid. A weak_ptr itself does not participate in the reference counting, and therefore, it cannot prevent the reference count from going to zero.
Note that the control block used by std::weak_ptr and std::shared_ptr is thread-safe: different non-atomic std::weak_ptr objects can be accessed using mutable operations, such as operator= or reset , simultaneously by multiple threads, even when these instances are copies or otherwise share the same control block ...
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.
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.
So is passing
std::weak_ptr
to functions 100% useless?
No.
Consider this toy example.
struct PointerObserver
{
std::weak_ptr<int> held_pointer;
void observe( std::weak_ptr<int> p )
{
held_pointer = std::move(p);
}
void report() const
{
if ( auto sp = held_pointer.lock() )
{
std::cout << "Pointer points to " << *sp << "\n";
}
else
{
std::cout << "Pointer has expired.\n";
}
}
};
In this example, a member function observe
keeps the weak_ptr
passed to it. This is often called a "sink parameter".
This weak_ptr
parameter communicates that this passed pointer is not owning, but reserves the ability to own at a later time, safely detecting if the pointer has expired.
As a different example, a function that doesn't hold state for later could also usefully receive a weak_ptr
parameter in a multithreaded context where the associated data might expire while the function is doing work.
If your clients have a weak_ptr
, and your logic could lock it or not, and is valid regardless, then pass a weak_ptr
.
As a concrete trivial example:
mutable std::mutex m_mutex;
mutable std::vector<std::weak_ptr<std::function<void(int)>>> m_callbacks;
void clean_callbacks(int x) {
auto l = std::unique_lock<std::mutex>(m_mutex);
auto it = std::remove_if( begin(m_callbacks), end(m_callbacks), [](auto w){ return !w.lock(); } );
m_callbacks.erase( it, end(m_callbacks) );
}
void call_callbacks() {
clean_callbacks();
auto tmp = [&]{
auto l = std::unique_lock<std::mutex>(m_mutex);
return m_callbacks;
}();
for (auto&& wf:tmp) {
if(auto sf = wf.lock()) {
(*sf)(x);
}
}
}
clean_callbacks
has a lambda that takes a weak_ptr
. It is used to remove any m_callbacks
whose lifetime has ended.
This code is used in a simple broadcaster where broadcasts happen far more often than listeners are invalidated, so waiting for the next broadcast to eliminate a dead listener is a good strategy.
Weak pointers are useful for holding on to objects that might not be available later on (without extending their lifetime). This means that they are usually used for storing in containers (or variables). One would usually pass a shared pointer until the object is stored and then converted to weak pointers. Then when used they must first be converted to shared pointers in order to check if they are still valid. Thus it's unlikely you will pass a weak pointer except as part of the storage and retrieval process, perhaps in helper functions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With