当定义一个lambda时,编译器生成了一个与lambda对应的类类型,这个类类型是未命名的。因此,当向一个函数传递一个lambda时,同时定义了一个类类型以及该类型的一个对象:传递的参数就是该对象。类似的,使用auto定义一个用lambda初始化的变量时,实际上定义了一个lambda生成的类类型对象。
auto f = [] {return 42};
cout << f() << endl; //打印42
类似于参数传递,lambda变量的捕获方式也可以是值捕获或引用捕获
值捕获
与参数不同,被捕获的变量的值是在lambda创建时拷贝,而不是调用时拷贝,因此,lambda创建后对值修改,不会影响到lambda内对应的值
int v1 = 42;
auto f = [v1] {return v1;};
v1 = 0;
cout << f() << endl; //输出42,f保存创建时的值
引用捕获
捕获变量时,可以采用引用捕获的方式
int v1 = 42;
auto f = [&v1] {return v1;};
v1 = 0;
cout << f() << endl; //输出0,采用引用捕获变量,而非拷贝
当以引用的方式捕获变量时,必须保证lambda执行时变量是存在的。
隐式捕获
除了显式列出捕获列表之外,还可以采用隐式捕获的方式,让编译器根据lambda体中的代码推断要使用那些变量,捕获变量的方式通过&或=来指定,&表示引用捕获,=表示值捕获。
auto wc = find_if(words.begin(), words.end(),
[=] (const string &s)) {return s.size() >= sz;};
当一部分采用值捕获,另一部分采用引用捕获时,可以混合使用隐式捕获和显式捕获
for_each(words.begin(), words.end(),
[&, c] (const string& s) {os << s << c};); //os隐式捕获, c显式捕获
注意:混合使用隐式和显式捕获时,捕获列表的第一个元素必须时&或=,也就是说,隐式捕获必须放在前面。
隐式捕获与显式捕获混合使用时,两种捕获的方式必须不同:
即隐式捕获采用引用捕获,则显式捕获必须采用值捕获,反之亦然。
可变lambda
通过值捕获的变量在lambda体内默认不可修改,如果想要修改,需要加关键字mutable
int = 42;
auto f = [m] () mutable {return ++m};;
cout << f() << endl; //输出43
通过引用捕获的变量是否可以修改依赖于引用的类型是否为const
指定lambda返回类型
lambda体内是单一返回类型时,无需指定返回类型,编译器可以推算出来;
但是lambda体内包含return之外的任何语句时,都必须指定返回类型,否则编译器假定lambda返回void,有时会产生编译错误
例如,将负数替换为绝对值的算法和lambda:
transform(v1.begin(), v1.end(), v1.begin(), [] (int i) {return i < 0 ? -i : i; }); //正确,只有单一的return语句
transform(v1.begin(), v1.end(), v1.begin(), [] (int i) {if(i < 0) return -i; else return i; }); //错误,不能推断返回类型
这时,需要指定返回类型,使用尾置返回类型指定
transform(v1.begin(), v1.end(), v1.begin(),
[] (int i) -> int {if(i < 0) return -i; else return i;});
注意:以上说明取自《C++ Primer(第5版)》,在VS2015中验证时发现,只要多条return语句返回的类型一致,就可以推断出返回类型,无须指定返回类型;
只有返回类型不一致时,才会产生编译错误:error C3487: “float”: 所有返回表达式必须推导为相同类型: 以前为“int”