我正在阅读this book第3章中的C ++ lambda部分,下面的代码让我感到困惑:
int x = 0;
int y = 42;
auto qqq = [x, &y] {
std::cout << "x: " << x << std::endl;
std::cout << "y: " << y << std::endl;
++y;
};
x = y = 77;
qqq();
qqq();
std::cout << "final y: " << y << std::endl;
此代码打印出来:
x: 0
y: 77
x: 0
y: 78
final y: 79
为什么qqq()没有注册x已经改为77?据说,通过值传递意味着我们可以读取但不能修改定义lambda的数据。这是否意味着我们无法在其定义后看到变化?
答案 0 :(得分:19)
这是因为当你定义lambda时,变量只被值(即复制)捕获一次。它可能不会像您所认为的那样“更新”。代码大致相当于:
#include <iostream>
int x = 0;
struct Lambda
{
int _internal_x; // this is used to "capture" x ONLY ONCE
Lambda(): _internal_x(x) {} // we "capture" it at construction, no updates after
void operator()() const
{
std::cout << _internal_x << std::endl;
}
} qqq;
int main()
{
qqq();
x = 77; // this has no effect on the internal state of the lambda
qqq();
}
答案 1 :(得分:11)
通过将值变量绑定到lambda闭包,您可以有效地将变量的值复制到lambda对象中的单独变量中。同样,通过引用绑定变量,您将使这样的内部变量成为对原始变量的引用,从而能够“看到原始变量的变化”。
这样看。以下代码......
#include <iostream>
int main()
{
int x = 0;
int y = 42;
auto qqq = [x, &y] {
std::cout << "x: " << x << std::endl;
std::cout << "y: " << y << std::endl;
++y;
};
x = y = 77;
qqq();
qqq();
std::cout << "final y: " << y << std::endl;
}
(...的种类,但不完全是)语法糖...
#include <iostream>
class MyLambda
{
private:
int x;
int& y;
public:
MyLambda(int x, int& y) : x(x), y(y) {}
void operator()()
{
std::cout << "x: " << x << std::endl;
std::cout << "y: " << y << std::endl;
++y;
}
};
int main()
{
int x = 0;
int y = 42;
MyLambda qqq = MyLambda(x, y);
x = y = 77;
qqq();
qqq();
std::cout << "final y: " << y << std::endl;
}
除了特殊的lambda语法和你不能直接引用lambda的类型这一事实。通过比较前者和后面的代码,您应该能够清楚地了解闭包。
答案 2 :(得分:4)
lambda使用lambda定义时的值(它创建一个副本),而不是在调用它时。
这可能会有所帮助:How are Lambda Closures Implemented?