lambda捕获的变量存储在哪里?

时间:2015-01-14 23:53:16

标签: c++ memory lambda

这个例子怎么可行?它会打印6

#include <iostream>
#include <functional>

using namespace std;

void scopeIt(std::function<int()> &fun) {
    int val = 6;
    fun = [=](){return val;}; //<-- this
}

int main() {
    std::function<int()> fun;

    scopeIt(fun);

    cout << fun();

    return 0;
}

调用6之后存储的值scopeIt在哪里?如果我将[=]替换为[&],则会打印0而不是6

4 个答案:

答案 0 :(得分:20)

它存储在闭包中,在你的代码中 - 然后存储在std::function<int()> &fun中。

lambda生成的东西等同于编译器生成的类的实例。

此代码:

[=](){return val;}

生成与此有效等效的内容......这将是“封闭”:

struct UNNAMED_TYPE
{
  UNNAMED_TYPE(int val) : val(val) {}
  const int val;
  // Above, your [=] "equals/copy" syntax means "find what variables
  //           are needed by the lambda and copy them into this object"

  int operator() () const { return val; }
  // Above, here is the code you provided

} (val);
// ^^^ note that this DECLARED type is being INSTANTIATED (constructed) too!!

答案 1 :(得分:16)

C ++中的Lambdas实际上只是“匿名”的struct functor。所以当你写这个:

int val = 6;
fun = [=](){return val;};

编译器将其转换为:

int val = 6;
struct __anonymous_struct_line_8 {
    int val;
    __anonymous_struct_line_8(int v) : val(v) {}

    int operator() () const {
        return val; // returns this->val
    }
};

fun = __anonymous_struct_line_8(val);

然后,std::function通过type erasure存储该仿函数。

当您使用[&]而不是[=]时,它会将结构更改为:

struct __anonymous_struct_line_8 {
    int& val; // Notice this is a reference now!
    ...

所以现在该对象存储了对函数val对象的引用,该对象在函数退出后变为悬空(无效)引用(并且您得到未定义的行为)。

答案 2 :(得分:7)

所谓的闭包类型(它是lambda表达式的类类型)具有每个捕获实体的成员。这些成员是按值捕获的对象,以及通过引用捕获的引用。它们使用捕获的实体初始化,并独立存在于闭包对象(此lambda指定的闭包类型的特定对象)中。

val的值捕获对应的未命名成员使用val初始化,并从闭包类型operator()内部访问,这很好。闭包对象可能很容易被复制或移动多次,直到发生这种情况,这也很好 - 闭包类型具有隐式定义的移动和复制构造函数,就像普通类一样。 但是,通过引用捕获时,在fun中调用main时隐式执行的左值到右值转换会导致未定义的行为,因为引用成员引用的对象已被销毁 - 即我们正在使用悬空参考。

答案 3 :(得分:3)

lambda表达式的值是 class 类型的对象,

  

对于每个实体   通过复制捕获,在闭包类型中声明了一个未命名的非静态数据成员。

(C ++ 11中的[expr.prim.lambda] / 14)

即lambda创建的对象

[=](){return val;}

实际上包含int类型的非静态成员,其值为6,并且此对象被复制到std::function对象中。