Binding to a weak_ptr

Scotty picture Scotty · Jul 27, 2012 · Viewed 9.9k times · Source

Is there a way to std::bind to a std::weak_ptr? I'd like to store a "weak function" callback that automatically "disconnects" when the callee is destroyed.

I know how to create a std::function using a shared_ptr:

std::function<void()> MyClass::GetCallback()
{
    return std::function<void()>(std::bind(&MyClass::CallbackFunc, shared_from_this()));
}

However the returned std::function keeps my object alive forever. So I'd like to bind it to a weak_ptr:

std::function<void()> MyClass::GetCallback()
{
    std::weak_ptr<MyClass> thisWeakPtr(shared_from_this());
    return std::function<void()>(std::bind(&MyClass::CallbackFunc, thisWeakPtr));
}

But that doesn't compile. (std::bind will accept no weak_ptr!) Is there any way to bind to a weak_ptr?

I've found discussions about this (see below), but there seems to be no standard implementation. What is the best solution for storing a "weak function", in particular if Boost is not available?


Discussions / research (all of these use Boost and are not standardized):

Answer

Nicol Bolas picture Nicol Bolas · Sep 1, 2012
std::weak_ptr<MyClass> thisWeakPtr(shared_from_this());
return std::function<void()>(std::bind(&MyClass::CallbackFunc, thisWeakPtr));

You should never do this. Ever.

MyClass::CallbackFunc is a non-static member function of the class MyClass. Being a non-static member function, it must be called with a valid instance of MyClass.

The entire point of weak_ptr is that it isn't necessarily valid. You can detect its validity by transforming it into a shared_ptr and then testing if the pointer is NULL. Since weak_ptr is not guaranteed to be valid at all times, you cannot call a non-static member function with one.

What you're doing is no more valid than:

std::bind(&MyClass::CallbackFunc, nullptr)

It may compile, but it will eventually crash when you try to call it.

Your best bet is to use actual logic, to not call the callback function if the weak_ptr is not valid. bind is not designed to do logic; it just does exactly what you tell it to: call the function. So you need to use a proper lambda:

std::weak_ptr<MyClass> thisWeakPtr(shared_from_this());
return std::function<void()>([thisWeakPtr]()
{
  auto myPtr = thisWeakPtr.lock();
  if(myPtr)
    myPtr->CallbackFunc()
});