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

Where are lambda captured variables stored?

这个例子怎么可能行得通?它打印 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;
}

scopeIt 调用完成后,值 6 存储在哪里?如果我用 [&] 替换 [=],它会打印 0 而不是 6

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

lambda 生成的内容等同于编译器生成的实例 class。

此代码:

[=](){return val;}

生成与此等效的内容...这将是 "closure":

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!!

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

For each entity captured by copy, an unnamed non-static data member is declared in the closure type.

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

即lambda创建的对象

[=](){return val;}

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

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

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

C++ 中的 Lambda 实际上只是 "anonymous" 结构仿函数。所以当你这样写的时候:

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 对象的引用,在函数退出后它变成了悬空(无效)引用(并且你会得到未定义的行为)。