何时捕获捕获变量?

时间:2018-03-08 20:23:59

标签: c++ c++11 lambda std-function

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

使用这些回调函数的代码需要一定的签名,因此假设我无法修改使用这些代码的代码,我需要坚持使用捕获变量而不是将事物作为函数参数传递。

何时被捕获?localVar?是在定义lambda还是调用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;
}

2 个答案:

答案 0 :(得分:8)

创建lambda时会捕获引用。永远不会捕获引用对象的。当你调用lambda时,它会使用引用来确定引用对象的值(无论何时使用它)(比如使用任何其他引用)。如果在引用的对象不再存在后使用引用,则使用的是悬空引用,它是未定义的行为。

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

std::cout << fs[i]() << "\n";调用了一个lambdas。但是,当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

了解相关信息

答案 1 :(得分:4)

  

当我尝试使用它们时,如果我通过引用

捕获它们,它们似乎超出了范围

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

在定义lambda的位置捕获“发生” - 这就是它的目的!如果它发生在以后,当你调用lambda时(那个时间?),你想要捕获的东西早就消失了,或者至少是无法到达的。

捕捉允许我们“保存”我们现在可以命名的内容,以供日后使用。但是如果你通过引用捕获,那么当你使用该引用时,你最好确保引用的东西仍然存在。

请注意像this这样的奇怪现象。