简体   繁体   English

Lambda:可以悬挂的副引用捕获

[英]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 when addDivisorFilter returns. lambda引用局部变量divisor ,但当addDivisorFilter返回时,该变量不再存在。 That's immediately after filters.emplace_back returns, so the function that's added to filters 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;
}

live example 实例

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM