V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
mer
V2EX  ›  C++

C++ 中 Lambda 对变量的捕获居然是在声明时就做了

  •  1
     
  •   mer · 2021-07-15 11:46:39 +08:00 · 2071 次点击
    这是一个创建于 988 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我看到在 C++ 14 特性中支持 Lambda 表达式捕获 move-only 的类型(原先是引用或值传递),于是写了一个小 demo:

    auto p = std::make_unique<int>(1);
    auto task1 = [p = move(p)](){ *p = 5; };
    cout << *p << endl;
    

    上述代码可以通过编译,但是运行时出现 core dump ,明显是因为此时 p 已经被 move 到 Lambda 表达式中去了,这里就迷惑,我一直以为要 auto task = [](){...}(); 或是 task1(); 执行时才会做参数的初始化(类似函数那样),但从结果来看,从声明的那一刻变量就已经被捕获了。

    虽然这一点对原本的值传递和引用捕获的参数来讲没有什么感知,但是对于捕获 move-only 类型的函数确实会产生一定的影响,因为从 Lambda 声明的这一刻开始你原本的外部变量就不能使用了。

    另外被捕获到 Lambda 内部的 move-only 变量是以类似 static 的状态存在的,即多次调用 task*p 的改变会累积,以这个 demo 可以看的比较清楚:

    auto p = std::make_unique<int>(1);
    auto task1 = [p = move(p)](){                                                 
        (*p)++;                                                                     
        cout << *p << endl;                                                                                                                                              
    }; 
    task1();
    task1();
    task1();
    
    6 条回复    2021-07-16 13:50:11 +08:00
    wutiantong
        1
    wutiantong  
       2021-07-15 11:55:42 +08:00   ❤️ 1
    lambda 就是这样的,
    你定义它时,你实际上是创建了一个匿名类型的 functor object,
    而捕获列表中定义的都是这个匿名类型的成员变量,
    所以,并不是 static 的,而是成员变量的。
    nightwitch
        2
    nightwitch  
       2021-07-15 12:49:42 +08:00   ❤️ 1
    其实仔细想想就会发现你的思路不合理。
    比如考虑一个 taskQueue,一个 lambda 会被推到这个队列里,而可能要等前面的任务都执行完毕以后才会执行。
    如果 lambda 不在声明的时候拷贝 /移动变量,那么当 taskQueue 排到的时候可能这个变量生命周期早结束了。

    lambda 其实是一个语法糖,本质上是声明一个重载了 operator()的 struct,auto task1 = []()... 类似于实例化这个结构体。
    ipwx
        3
    ipwx  
       2021-07-15 13:04:38 +08:00
    楼主可能和 JS 搞起来了。但是 JS 那种其实不是更奇怪吗。

    for (int i=0; i<10; ++i) {
    task_list.emplace([i] () { std::cout << i << std::endl; });
    }

    这种用法不挺符合常识吗?
    hitmanx
        4
    hitmanx  
       2021-07-15 13:38:36 +08:00
    ".. 但从结果来看,从声明的那一刻变量就已经被捕获了。虽然这一点对原本的值传递和引用捕获的参数来讲没有什么感知"..

    Really?以值 capture 一样有感知

    int main()
    {
    ....int a = 1;
    ....auto func = [a]() { std::cout << a << std::endl; };
    ....a = 2;
    ....func();
    ....return 0;
    }
    mer
        5
    mer  
    OP
       2021-07-15 14:06:04 +08:00
    @wutiantong 原来是这样
    @nightwitch 懂了
    @ipwx 没用过 JS
    @hitmanx 噢 我说的值传递不是这个意思,忘了考虑这种值 Capture 的,一般用 引用 capture 和,值传参
    aneostart173
        6
    aneostart173  
       2021-07-16 13:50:11 +08:00
    跟 rust 一样啦?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2563 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 15:41 · PVG 23:41 · LAX 08:41 · JFK 11:41
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.