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

2021-07-15 11:46:39 +08:00
 mer

我看到在 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();
2280 次点击
所在节点    C++
6 条回复
wutiantong
2021-07-15 11:55:42 +08:00
lambda 就是这样的,
你定义它时,你实际上是创建了一个匿名类型的 functor object,
而捕获列表中定义的都是这个匿名类型的成员变量,
所以,并不是 static 的,而是成员变量的。
nightwitch
2021-07-15 12:49:42 +08:00
其实仔细想想就会发现你的思路不合理。
比如考虑一个 taskQueue,一个 lambda 会被推到这个队列里,而可能要等前面的任务都执行完毕以后才会执行。
如果 lambda 不在声明的时候拷贝 /移动变量,那么当 taskQueue 排到的时候可能这个变量生命周期早结束了。

lambda 其实是一个语法糖,本质上是声明一个重载了 operator()的 struct,auto task1 = []()... 类似于实例化这个结构体。
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
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
2021-07-15 14:06:04 +08:00
@wutiantong 原来是这样
@nightwitch 懂了
@ipwx 没用过 JS
@hitmanx 噢 我说的值传递不是这个意思,忘了考虑这种值 Capture 的,一般用 引用 capture 和,值传参
aneostart173
2021-07-16 13:50:11 +08:00
跟 rust 一样啦?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/789667

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX