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

BWG*_*BWG 25 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;
}
Run Code Online (Sandbox Code Playgroud)

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

Dre*_*ann 20

It is stored within the closure, which - in your code - is then stored within std::function<int()> &fun.

A lambda generates what's equivalent to an instance of a compiler generated class.

This code:

[=](){return val;}
Run Code Online (Sandbox Code Playgroud)

Generates what's effectively equivalent to this... this would be the "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!!
Run Code Online (Sandbox Code Playgroud)

  • 我认为比_how_存储闭包更难的问题是_where_它被存储.看起来它不能存储在运行时堆栈中,因为闭包是在`scopeIt`内构建的,但必须从它返回.所以它应该存储在动态存储中,在赋值给`fun`时分配.但是这个存储必须在`main`结束时释放; 但是可能会从`scopeIt`单独编译,因此无法了解`UNNAMED_TYPE`的结构.`std :: function`如何设法摧毁它的目标?幕后是否有一些多态性? (4认同)
  • @MarcvanLeeuwen你可能想要阅读关于类型擦除的内容,这实际上是`std :: function`中幕后发生的事情.IOW,是的,`std :: function`包含一个动态分配的对象,其中包含一个虚拟析构函数.可以通过其中一个构造函数来定制该对象的动态分配`std :: allocator_arg_t`:http://en.cppreference.com/w/cpp/utility/functional/function/function (2认同)

Cor*_*lks 16

C++中的Lambdas实际上只是"匿名"结构函子.所以当你写这个:

int val = 6;
fun = [=](){return val;};
Run Code Online (Sandbox Code Playgroud)

编译器将其转换为:

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);
Run Code Online (Sandbox Code Playgroud)

然后,std::function通过类型擦除存储该仿函数.

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

struct __anonymous_struct_line_8 {
    int& val; // Notice this is a reference now!
    ...
Run Code Online (Sandbox Code Playgroud)

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


Col*_*mbo 7

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

与值捕获相对应的未命名成员使用闭包类型的内部进行val初始化val和访问operator(),这很好.闭包对象可能很容易被复制或移动多次,直到发生这种情况,这也很好 - 闭包类型具有隐式定义的移动和复制构造函数,就像普通类一样.
然而,通过参考捕获时,在调用时被隐式执行左值到右值转换funmain诱导未定义的行为作为该基准部件称为已经被销毁的对象 -即,我们使用的是悬空参考.