lambda表达式源于函数式编程的概念,它可以就地匿名定义目标函数或函数对象,不需要额外写一个命名函数或者函数对象。lambda表达式的类型在C++11中被称为“闭包类型”,也可以理解为是一个仿函数(带operator()类),其语法形式如下:
[capture] (params) opt -> ret {body;};
capture: 捕获列表;
params: 参数列表;
opt: 函数选项;
ret: 返回值类型;
body: 函数体。
很多时候,lambda表达式的返回值是非常明显的,C++11中允许省略lambda表达式的返回值定义,即去掉“-> ret”部分,这样编译器就会自动推导出返回值类型。一个简单的lambda表达式如下:
#include "stdafx.h" #include<iostream>// std::cout int main() { auto f = [](int a, int b) -> int {return (a*b); }; std::cout << f(3, 4) << std::endl; // output:12 return 0; }
lambda表达式中的捕获字段非常有用,捕获的意思是捕获外部变量在lambda函数体内部使用,有以下几种不同的捕获方式:
[]:不捕获外部的任何变量,如果在函数体内使用外部变量就会报错;
[&]:以引用的方式捕获外部的所有变量,如果在函数体内改变外部变量值,外部变量值就会随之改变;
[=]:以值的方式捕获外部的所有变量,如果在函数体内改变“外部变量值”,外部变量值不会改变;
[=, &foo]:以值得方式捕获外部所有变量,并以引用的方式捕获foo变量;
[bar]:以值得方式捕获bar变量,其他外部变量不捕获;
[this]:捕获当前类中的this指针,让lambda表达式拥有和当前类成员函数同样的访问权限。如果已经使用了&或者=,则this是默认添加的。
注意按值捕获和按引用捕获的区别,按值捕获将在lambda表达式中用到的外部变量进行了一份拷贝并且保存到了lambda闭包类型中,如果之后对所用到的外部变量进行修改,lambda表达式中用到的变量是不会改变的,因为该变量只是一份拷贝;按引用捕获则是直接对变量的值进行读取或操作,如代码所示:
#include "stdafx.h" #include<iostream>// std::cout int main() { int numA = 0; int numB = 0; { auto f1 = [] {return numA; };// error,没有捕获外部变量 auto f2 = [&numB] {return (--numB); }; f2(); std::cout << numB << std::endl;// -1 } int numC = 100; int numD = 100; auto f3 = [=]() -> int {return (numC*numD); }; numD = 99; std::cout << f3() << std::endl; // output:10000 return 0; }
有了lambda表达式,我们在使用for循环或者std::for_each的时候,就可以改写成如下形式:
#include "stdafx.h" #include<iostream>// std::cout #include<list> #include <algorithm> int main() { std::list<int> l = { 1, 3, 5, 87, 64, 33, 0, -1, -100}; int nCount = 0; std::for_each(l.begin(), l.end(), [&nCount](int val) {if (val > 50) nCount++; }); std::cout << "The number larger than 50 in l is: " << nCount << std::endl; return 0; }
总结一下,lambda表达式看起来和Java里边的匿名函数比较像,使用lambda表达式后,可以使程序变得非常简洁。但是,如果使用的lambda表达式包含的内容过多,或者lambda表达式的实体是好多地方都会用到的功能性函数,还是单独列出一个函数比较方便。