Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it useful to pass std::weak_ptr to functions?

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?

like image 526
Ignorant Avatar asked Dec 03 '18 17:12

Ignorant


People also ask

Why do we need Weak_ptr?

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.

Is std :: Weak_ptr thread safe?

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 ...

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.

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.


3 Answers

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.

like image 191
Drew Dormann Avatar answered Oct 19 '22 16:10

Drew Dormann


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.

like image 30
Yakk - Adam Nevraumont Avatar answered Oct 19 '22 15:10

Yakk - Adam Nevraumont


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.

like image 1
Motti Avatar answered Oct 19 '22 17:10

Motti