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

Ama*_*eus 7 c++ lambda c++11 c++14

有效的现代C++中的Scott Meyers在lambda章节中说:

请考虑以下代码:

void addDivisorFilter()
{
    auto calc1 = computeSomeValue1();
    auto calc2 = computeSomeValue2();

    auto divisor = computeDivisor(calc1, calc2);

    filters.emplace_back(
          [&](int value) { return value % divisor == 0; }
    );
}
Run Code Online (Sandbox Code Playgroud)

这段代码是一个等待发生的问题.lambda引用局部变量divisor,但addDivisorFilter返回时该变量不再存在.这是在filters.emplace_back返回后立即进行的,因此添加的功能filters在到达时基本上是死的.使用该过滤器几乎从创建时开始产生未定义的行为.

问题是:为什么它是一个未定义的行为?据我所知,filters.emplace_back只有在lambda表达式完成后才返回,并且在执行期间,它divisor是有效的.

更新

我错过的重要数据包括:

using FilterContainer = std::vector<std::function<bool(int)>>;
FilterContainer filters;
Run Code Online (Sandbox Code Playgroud)

vso*_*tco 6

那是因为向量的范围filters超过了函数之一.在函数出口处,向量filters仍然存在,捕获的引用divisor现在正在悬空.

根据我的理解,filters.emplace_back仅在lambda表达式完成后返回,并且在执行期间,除数有效.

这不是真的.向量存储从闭包创建的lambda,并且不"执行"lambda,在函数退出后执行lambda.从技术上讲,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
}
Run Code Online (Sandbox Code Playgroud)