什么时候捕获捕获变量?

When are capture variables captured?

我正在制作一个 std::vector 的回调 std::function,我在理解捕获时遇到了一些困难。如果我通过引用捕获,当我尝试使用它们时,它们似乎超出了范围。如果我按价值捕获,一切正常。

使用这些回调函数的代码需要特定的签名,所以假设我不能修改使用这些函数的代码,我需要坚持使用捕获变量而不是将东西作为函数参数传递。

什么时候 localVar 被捕获了?是在定义 lambda 时还是在调用时?答案是否会根据我是按值捕获还是按引用捕获而改变?

这里有一个我想理解的小例子:

#include <iostream>
#include <functional>
#include <vector>

int main(int argc, char **argv)
{

    int n(5);

    // make a vector of lambda functions
    std::vector<std::function<const int(void)> > fs;
    for(size_t i = 0; i < n; ++i){
        int localVar = i;
        auto my_lambda = [&localVar]()->int // change &localVar to localVar and it works
        {
            return localVar+100;
        };
        fs.push_back(my_lambda);
    }

    // use the vector of lambda functions
    for(size_t i = 0; i < n; ++i){
        std::cout << fs[i]() << "\n";
    }


    return 0;
}

创建 lambda 时会捕获 reference。引用对象的 value 永远不会被捕获。当您调用 lambda 时,无论您何时使用它(就像使用任何其他引用一样),它都会使用引用来确定引用对象的值。如果在引用的对象不复存在后使用引用,则您使用的是悬空引用,这是未定义的行为。

在这种情况下,auto my_lambda = [&localVar]()->int 创建了一个 lambda,其中引用名为 localVar 的局部变量 localVar

std::cout << fs[i]() << "\n"; 调用其中一个 lambda。但是,当 lambda 执行 return localVar+100; 时,它试图使用对局部变量 localVar 的引用 localVar(第一个 for 循环的局部变量),但该局部变量不再存在。您有未定义的行为。

如果您删除与号并按值获取 localVar (auto my_lambda = [localVar]()->int),您将捕获创建 lambda 时值的副本。由于它是副本,因此原始 localVar.

发生什么并不重要

您可以在 http://en.cppreference.com/w/cpp/language/lambda#Lambda_capture

阅读相关内容

They seem to be going out of scope when I try to use them if I capture by reference

没错。您创建了一个 lambda,它封装了对局部变量的引用。变量超出范围,使该引用悬空。这与任何其他参考文献没有什么不同。

在您定义 lambda 的地方捕获 "happens" — 这就是它的目的!如果稍后发生,当你调用 lambda 时(哪一次?),你想要捕获的东西早就消失了,或者至少无法访问。

捕获使我们能够 "save" 我们现在可以命名的东西,以备后用。但是如果你通过引用捕获,你最好确保当你使用那个引用时 referred-to 仍然存在。

不过要注意像 这样的怪事。