【发布时间】:2021-10-14 11:38:05
【问题描述】:
如问题lambda capture by value mutable doesn't work with const &? 中所见,当在可变lambda 中使用其名称或[=] 捕获类型为const T& 的值时,隐藏类中的字段的类型为const T。可以说,对于可变 lambda,这是正确的做法。
但是为什么对非可变 lambda 也这样做呢?在非可变 lambda 中,operator()(...) 被声明为 const,因此无论如何它都无法修改捕获的值。
当我们移动 lambda 时,会发生这种不良后果,例如将其包装在 std::function 中时。
请看以下两个例子:
#include <cstdio>
#include <functional>
std::function<void()> f1, f2;
struct Test {
Test() {puts("Construct");}
Test(const Test& o) {puts("Copy");}
Test(Test&& o) {puts("Move");}
~Test() {puts("Destruct");}
};
void set_f1(const Test& v) {
f1 = [v] () {}; // field type in lambda object will be "const Test"
}
void set_f2(const Test& v) {
f2 = [v = v] () {}; // field type in lambda object will be "Test"
}
int main() {
Test t;
puts("set_f1:");
set_f1(t);
puts("set_f2:");
set_f2(t);
puts("done");
}
我们得到以下编译器生成的 lambda 类:
class set_f1_lambda {
const Test v;
public:
void operator()() const {}
};
class set_f2_lambda {
Test v;
public:
void operator()() const {}
};
程序打印以下内容(使用 gcc 或 clang):
Construct
set_f1:
Copy
Copy
Copy
Destruct
Destruct
set_f2:
Copy
Move
Move
Destruct
Destruct
done
Destruct
Destruct
Destruct
v 值在第一个示例set_f1 中被复制不少于三次。
在第二个示例set_f2 中,唯一的副本是在捕获值时(如预期的那样)。使用两个动作的事实是 libstdc++ 中的一个实现细节。第一个动作发生在operator= 内部std::function 当按值将仿函数传递给内部函数时(为什么这个函数签名不使用传递引用?)。第二步发生在 move 构造最终的堆分配函子时。
但是,如果字段是const,则 lambda 仿函数对象的移动构造函数不能对字段使用移动构造函数(因为这样的构造函数在窃取其内容后无法“清除” const 变量) .这就是为什么必须对这些字段使用复制构造函数。
所以对我来说,在非可变 lambda 中将值捕获为 const 似乎只会产生负面影响。我是否遗漏了一些重要的东西,或者只是以这种方式标准化以使标准更简单?
【问题讨论】:
-
类似问题没有满意答案:stackoverflow.com/questions/56811624/…
标签: c++ lambda constants language-lawyer mutable