C++11标准中引入了 Lambda 表达式,用于定义匿名函数,使得代码更加灵活简洁。
Lambda 表达式与普通函数类似,也有参数列表、返回值类型和函数体,只是它的定义方式更简洁,并且可以在函数内部定义。
1 Lambda简介
-
lambda表达式是C++11最重要也最常用的一个特性之一,其实在C#3.5中就引入了lambda,Java8中也引入了lambda表达式
-
lambda 来源于函数式编程的概念,也是现在编程语言的一个特点,有如下优点:
-
声明式编程风格:就地匿名定义目标函数或函数对象,不需要额外写一个命名函数或者对象,以更直接的方式去写程序,好的可读性和可维护性
-
简洁:不需要额外再写一个函数或者函数对象,避免了代码膨胀和功能分散,让开发者更加集中精力在手边的问题,同时也获取了更高的生产率
-
在需要的时间和地点实现功能闭包,使程序更灵活
-
-
lambda表达式是一种局部类类型,它含有一个构造函数,和一个const成员函数operator()()。
-
lambda表达式除了能做参数外,还能用于初始化一个声明为
auto
或者std::function<R(LA)>
的变量。R是lambda的返回类型,LA
是它的类型参数列表。
2 Lambda 使用
-
Lambda的语法形式如下:
[capture](params) -> return_type { body };
对应解释为:
[捕获列表] (函数参数) mutable 或 throw() 声明 -> 返回值类型 {函数体}
由上可以看出Lamda表达的组成部件为:
-
一个可能为空的捕获列表,指明定义环境中哪些名字能够被用在lambda表达式中,以及这些名字是被拷贝还是引用。捕获列表位于[]中。 补充: 静态变量不能也不需要被捕获。
-
一个可选的参数列表。指明所需要的参数,位于()中。补充: 当无参数时,可以省略。
-
一个可选的mutable修饰符,当需要修改通过值捕获的变量的副本时,位于()之后。
-
一个可选的
throw()
声明或者noexcept
,表明无异常被抛出。位于mutable修饰符之后。 -
一个可选的->形式的返回类型声明,可与decltype一起搭配使用。
-
一个表达式体,指明要执行的代码,位于{}块中。
-
-
使用示例:
int main() { auto f1 = [](int a) -> int {return a + 1; }; //捕获列表 auto f2 = [](int a) {return a; }; //直接推导类型 可以省略 -> int auto f3 = [](int a) ->std::initializer_list<int> {return { a,1 }; }; //auto f = [](int a) {return { a,1 }; }; 无法推导出 }
-
变量捕获
Lambda表达式可以通过
[ ]
捕获其父级作用域的变量,具体使用如下:-
[]
不捕获任何变量 -
[&]
捕获外部作用域中所有变量,并作为引用在函数体中使用(按引用捕获) -
[=]
捕获外部作用域中所有变量,并作为副本在函数体中使用(按值捕获), 即以const引用的方式传值 -
[=,&foo]
按值捕获外部作用域中所有变量,并按引用获取foo变量 -
[bar]
按值捕获bar变量,同时不捕获其他变量 -
[this]
捕获当前类中的this指针,让lambda表达式拥有和当前类成员同样的访问权限,如果已经使用了&或者=,就默认添加此选项,捕获this的目的是可以在lambda中使用当前类的成员函数和成员变量
-
-
mutable
使用当函数参数以值引用传递方式传递时,在函数体内是不可以修改该函数参数的值的,我们可以使用mutable修饰符,使得该函数参数可以在函数体内改变。
示例如下:
int main() { int a = 0; auto f1 = [=] {return a++; }; //error auto f2 = [=]() mutable {return a++; }; //可变的 //这里的a是上面a的一个副本 }
-
返回类型
lambda表达式的大多数规则都是从类和函数借鉴过来的,然而需要有两点需要注意:
-
如果lambda表达式不需要任何参数,则参数列表可以省略。因此lambda表达式的最简形式是[]{}。
-
lambda表达式的返回类型可以有其本身推断得到,而函数(方法)不行。
如果在一条lambda表达式的主体部分不包含return语句,则其返回类型为void。
如果lambda表达式只包含一条return语句,则返回类型是return表达式的类型。 其他情况,必须显式定义一个返回类型。
-