Default by-reference capture can lead to dangling references; default by-value capture is susceptible to dangling pointers, while misleadingly susggests the lambdas are self-contained.
Default by-reference capture
If the lifetime of a closure created from a lambda exceeds the lifetime of the local variable or parameter captured by-reference, the reference in the closure will dangle:
|
|
Long-term, it’s better software engineering to explicitly list the local variables and parameters that a lambda depends on.
Default by-value capture
Capture by value will solve the dangling problem in above example, but it can’t guarantee the safety if we capture a pointer and that pointer is deleted outside the lambda, which causes our copied pointer to dangle. This usually happens where this
, a raw pointer, implicitly shows up, for example:
|
|
Captures apply only to non-static local variables (including parameters) visible in the scope where the lambda is created. Since diviser
above is a data member of the Widget
class, instead of a local variable, what compilers see is as if it had been written as follows:
|
|
and now consider this:
|
|
In this code above, a filter is created containing a copy of a this
pointer to the newly created Widget
, and then we add this filter to filters
. When doSomeWork
finishes, the Widget
object is destroyed by std::unique_ptr
, and filters
contains an entry with a dangling pointer.
To solve the problem, just make a local copy of the data member we want to capture and then capture the copy:
|
|
In C++14, a better way to capture a data member is to use generalized lambda capture (Item 32):
|
|