[英]Lambda: A by-reference capture that could dangle
Scott Meyers, in Effective Modern C++, says, at lambda chapter, that: 有效的现代C ++中的Scott Meyers在lambda章节中说:
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 problem waiting to happen. 这段代码是一个等待发生的问题。 The lambda refers to the local variable
divisor
, but that variable ceases to exist whenaddDivisorFilter
returns. lambda引用局部变量divisor
,但当addDivisorFilter
返回时,该变量不再存在。 That's immediately afterfilters.emplace_back
returns, so the function that's added tofilters
is essentially dead on arrival. 这是在filters.emplace_back
返回后立即执行的,因此添加到filters
的函数在到达时基本上是死的。 Using that filter yields undefined behaviour from virtually the moment it's created. 使用该过滤器几乎从创建时开始产生未定义的行为。
The question is: Why is it an undefined behaviour? 问题是:为什么它是一个未定义的行为? For what I understand, filters.emplace_back
only returns after lambda expression is complete, and, during it execution, divisor
is valid. 根据我的理解, filters.emplace_back
仅在lambda表达式完成后返回,并且在执行期间, divisor
有效。
Update 更新
An important data that I've missed to include is: 我错过的重要数据包括:
using FilterContainer = std::vector<std::function<bool(int)>>;
FilterContainer filters;
That's because the scope of the vector filters
outlives the one of the function. 那是因为向量filters
的范围超过了其中一个函数。 At function exit, the vector filters
still exists, and the captured reference to divisor
is now dangling. 在函数出口处,向量filters
仍然存在,并且捕获的对divisor
引用现在是悬空的。
For what I understand, filters.emplace_back only returns after lambda expression is complete, and, during it execution, divisor is valid. 根据我的理解,filters.emplace_back仅在lambda表达式完成后返回,并且在执行期间,除数有效。
That's not true. 这不是真的。 The vector stores the lambda created from the closure, and does not "execute" the lambda, you execute the lambda after the function exits. 向量存储从闭包创建的lambda,并且不“执行”lambda,在函数退出后执行lambda。 Technically the lambda is constructed from a closure (an compiler-dependent-named class) that uses a reference internally, like 从技术上讲,lambda是从一个内部使用引用的闭包(一个依赖于编译器的类)构造的
#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
}
You are not evaluating the lambda function while addDivisorFilter is active. 在addDivisorFilter处于活动状态时,您没有评估lambda函数。 You are simply adding "the function" to the collection, not knowing when it might be evaluated (possibly long after addDivisorFilter returned). 您只是将“函数”添加到集合中,而不知道何时可以对其进行评估(可能在addDivisorFilter返回后很长时间)。
In addition to @vsoftco's answer, the following modified example code lets you experience the problem: 除了@ vsoftco的答案之外,以下修改过的示例代码可以让您体验到这个问题:
#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;
}
This example results in a Floating point exception
at runtime, since the reference to divisor
is not valid when the lambda is evaluated in main
. 此示例在运行时导致Floating point exception
,因为当在main
计算lambda时,对divisor
的引用无效。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.