I've come to C++11 from an Objective-C background, and one thing I'm struggling to come to terms with is the different capturing semantics of C++11 lambdas vs Objective-C "blocks". (See here for a comparison).
In Objective-C, like C++, the self
/this
pointer is implicitly captured if you refer to a member variable. But because all objects in Objective-C are effectively "shared pointers", to use the C++ terminology, you can do this:
doSomethingAsynchronously(^{
someMember_ = 42;
});
... and you're guaranteed that the object whose member you're accessing will be alive when the block executes. You don't have to think about it. The equivalent in C++ seems to be something like:
// I'm assuming here that `this` derives from std::enable_shared_from_this and
// is already owned by some shared_ptr.
auto strongThis = shared_from_this();
doSomethingAsynchronously([strongThis, this] {
someMember_ = 42; // safe, as the lambda holds a reference to this
// via shared_ptr.
});
Here, you need to remember to capture the shared_ptr in addition to the this pointer. Is there some less error-prone way of achieving this?
Lambdas can both capture variables and accept input parameters. A parameter list (lambda declarator in the Standard syntax) is optional and in most aspects resembles the parameter list for a function. auto y = [] (int first, int second) { return first + second; };
A capture clause of lambda definition is used to specify which variables are captured and whether they are captured by reference or by value. An empty capture closure [ ], indicates that no variables are used by lambda which means it can only access variables that are local to it.
C++ Lambda expression allows us to define anonymous function objects (functors) which can either be used inline or passed as an argument. Lambda expression was introduced in C++11 for creating anonymous functors in a more convenient and concise way.
Basically, lambdas are syntactic sugar, designed to reduce a lot of the work required in creating ad-hoc functor classes. The brackets ([]) mark the declaration of the lambda; it can have parameters, and it should be followed by its body (the same as any other function).
One of the founding principles of C++ is that you don't pay for what you don't use. That means in this case that contexts where taking a shared_ptr
to this
is unnecessary shouldn't incur any reference counting overhead. This also means that it shouldn't happen automatically even e.g. as a feature of enable_shared_from_this
, since you might want to pass a short-lived lambda to an algorithm (for_each
, etc.) in which case the lambda doesn't outlive its scope.
I'd suggest adapting the lambda-wrapper pattern; in that case it's used for move
capture of a large object (How to capture std::unique_ptr "by move" for a lambda in std::for_each), but it can equally be used for shared capture of this
:
template<typename T, typename F>
class shared_this_lambda {
std::shared_ptr<T> t; // just for lifetime
F f;
public:
shared_this_lambda(std::shared_ptr<T> t, F f): t(t), f(f) {}
template<class... Args>
auto operator()(Args &&...args)
-> decltype(this->f(std::forward<Args>(args)...)) {
return f(std::forward<Args>(args)...);
}
};
template<typename T>
struct enable_shared_this_lambda {
static_assert(std::is_base_of<std::enable_shared_from_this<T>, T>::value,
"T must inherit enable_shared_from_this<T>");
template<typename F>
auto make_shared_this_lambda(F f) -> shared_this_lambda<T, F> {
return shared_this_lambda<T, F>(
static_cast<T *>(this)->shared_from_this(), f);
}
template<typename F>
auto make_shared_this_lambda(F f) const -> shared_this_lambda<const T, F> {
return shared_this_lambda<const T, F>(
static_cast<const T *>(this)->shared_from_this(), f);
}
};
Use by inheriting enable_shared_this_lambda
in addition to enable_shared_from_this
; you can then explicitly request that any long-lived lambdas take a shared this
:
doSomethingAsynchronously(make_shared_this_lambda([this] {
someMember_ = 42;
}));
Actually, there's one right answer to this problem. The answer has the exact same effect of binding with shared_from_this()
(like when you do it with boost::asio::io_service
). Think about it; what does binding with shared_from_this()
do? It simple replaces this
. So what prevents you from replacing this
with shared_from_this()
totally?
Following your example, which I updated to make the difference clearer, instead of this:
auto strongThis = shared_from_this();
doSomethingAsynchronously([strongThis, this] () {
this->someMember_ = 42; //here, you're using `this`... that's wrong!
});
Do this:
auto strongThis = shared_from_this();
doSomethingAsynchronously([strongThis] () //notice, you're not passing `this`!
{
strongThis->someMember_ = 42;
});
The only cost here is that you're gonna have to prefix everything with strongThis->
. But this is the most meaningful way to do it.
Boost uses:
auto self(shared_from_this());
auto l = [this, self] { do(); };
Mentioned here: What's the reason of using auto self(shared_from_this()) variable in lambda function?
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