这个例子怎么可行?它会打印6
:
#include <iostream>
#include <functional>
using namespace std;
void scopeIt(std::function<int()> &fun) {
int val = 6;
fun = [=](){return val;}; //<-- this
}
int main() {
std::function<int()> fun;
scopeIt(fun);
cout << fun();
return 0;
}
调用6
之后存储的值scopeIt
在哪里?如果我将[=]
替换为[&]
,则会打印0
而不是6
。
答案 0 :(得分:20)
它存储在闭包中,在你的代码中 - 然后存储在std::function<int()> &fun
中。
lambda生成的东西等同于编译器生成的类的实例。
此代码:
[=](){return val;}
生成与此有效等效的内容......这将是“封闭”:
struct UNNAMED_TYPE
{
UNNAMED_TYPE(int val) : val(val) {}
const int val;
// Above, your [=] "equals/copy" syntax means "find what variables
// are needed by the lambda and copy them into this object"
int operator() () const { return val; }
// Above, here is the code you provided
} (val);
// ^^^ note that this DECLARED type is being INSTANTIATED (constructed) too!!
答案 1 :(得分:16)
C ++中的Lambdas实际上只是“匿名”的struct functor。所以当你写这个:
int val = 6;
fun = [=](){return val;};
编译器将其转换为:
int val = 6;
struct __anonymous_struct_line_8 {
int val;
__anonymous_struct_line_8(int v) : val(v) {}
int operator() () const {
return val; // returns this->val
}
};
fun = __anonymous_struct_line_8(val);
然后,std::function
通过type erasure存储该仿函数。
当您使用[&]
而不是[=]
时,它会将结构更改为:
struct __anonymous_struct_line_8 {
int& val; // Notice this is a reference now!
...
所以现在该对象存储了对函数val
对象的引用,该对象在函数退出后变为悬空(无效)引用(并且您得到未定义的行为)。
答案 2 :(得分:7)
所谓的闭包类型(它是lambda表达式的类类型)具有每个捕获实体的成员。这些成员是按值捕获的对象,以及通过引用捕获的引用。它们使用捕获的实体初始化,并独立存在于闭包对象(此lambda指定的闭包类型的特定对象)中。
与val
的值捕获对应的未命名成员使用val
初始化,并从闭包类型operator()
内部访问,这很好。闭包对象可能很容易被复制或移动多次,直到发生这种情况,这也很好 - 闭包类型具有隐式定义的移动和复制构造函数,就像普通类一样。
但是,通过引用捕获时,在fun
中调用main
时隐式执行的左值到右值转换会导致未定义的行为,因为引用成员引用的对象已被销毁 - 即我们正在使用悬空参考。
答案 3 :(得分:3)
lambda表达式的值是 class 类型的对象,
对于每个实体 通过复制捕获,在闭包类型中声明了一个未命名的非静态数据成员。
(C ++ 11中的[expr.prim.lambda] / 14)
即lambda创建的对象
[=](){return val;}
实际上包含int
类型的非静态成员,其值为6,并且此对象被复制到std::function
对象中。