Lambda: capture by reference, which can hang

Scott Meyers, in Effective Modern C ++, says in the lambda chapter that:

Consider the following code:

void addDivisorFilter() { auto calc1 = computeSomeValue1(); auto calc2 = computeSomeValue2(); auto divisor = computeDivisor(calc1, calc2); filters.emplace_back( [&](int value) { return value % divisor == 0; } ); } 

This code is a pending issue. Lambda refers to the local variable divisor , but this variable ceases to exist when addDivisorFilter returned. That immediately after filters.emplace_back returns, so the function added to filters is essentially dead upon arrival. Using this filter gives undefined behavior almost from the moment it was created.

Question: why is this behavior undefined? It is clear that filters.emplace_back returned only after the lambda expression is completed and the divisor valid during its execution.

Update

The important data that I skipped is the following:

 using FilterContainer = std::vector<std::function<bool(int)>>; FilterContainer filters; 
+6
source share
3 answers

This is because the scope of the filters vector survives one of the functions. When the function exits, the filters vector still exists, and the captured divisor reference now hangs.

For what I understand, filter.emplace_back is returned only after the lambda expression is complete and during its execution the divisor is valid.

This is not true. The vector stores the lambda created from the closure and does not perform the lambda, you execute the lambda after the function exits. Technically, a lambda is built from a closure (a compiler dependent class) that uses an internal reference, e.g.

 #include <vector> #include <functional> struct _AnonymousClosure { int& _divisor; // this is what the lambda captures bool operator()(int value) { return value % _divisor == 0; } }; int main() { std::vector<std::function<bool(int)>> filters; // local scope { int divisor = 42; filters.emplace_back(_AnonymousClosure{divisor}); } // UB here when using filters, as the reference to divisor dangle } 
+6
source

You do not evaluate the lambda function while addDivisorFilter is active. You simply add a β€œfunction” to the collection, not knowing when it can be evaluated (perhaps long after addDivisorFilter returns).

+1
source

In addition to @vsoftco's answer, the following modified example code allows you to experience the problem:

 #include <iostream> #include <functional> #include <vector> void addDivisorFilter(std::vector<std::function<int(int)>>& filters) { int divisor = 5; filters.emplace_back( [&](int value) { return value % divisor == 0; } ); } int main() { std::vector<std::function<int(int)>> filters; addDivisorFilter(filters); std::cout << std::boolalpha << filters[0](10) << std::endl; return 0; } 

living example

This example raises a Floating point exception at runtime since the divisor reference is invalid when the lambda is evaluated in main .

+1
source

All Articles