我看到在 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();
1
wutiantong 2021-07-15 11:55:42 +08:00 1
lambda 就是这样的,
你定义它时,你实际上是创建了一个匿名类型的 functor object, 而捕获列表中定义的都是这个匿名类型的成员变量, 所以,并不是 static 的,而是成员变量的。 |
2
nightwitch 2021-07-15 12:49:42 +08:00 1
其实仔细想想就会发现你的思路不合理。
比如考虑一个 taskQueue,一个 lambda 会被推到这个队列里,而可能要等前面的任务都执行完毕以后才会执行。 如果 lambda 不在声明的时候拷贝 /移动变量,那么当 taskQueue 排到的时候可能这个变量生命周期早结束了。 lambda 其实是一个语法糖,本质上是声明一个重载了 operator()的 struct,auto task1 = []()... 类似于实例化这个结构体。 |
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; }); } 这种用法不挺符合常识吗? |
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; } |
5
mer OP @wutiantong 原来是这样
@nightwitch 懂了 @ipwx 没用过 JS @hitmanx 噢 我说的值传递不是这个意思,忘了考虑这种值 Capture 的,一般用 引用 capture 和,值传参 |
6
aneostart173 2021-07-16 13:50:11 +08:00
跟 rust 一样啦?
|