一直以来,lambda仿佛都是像是脚本语言的专利,对于C/C++这类系统级强类型的编译语言来说,实现匿名函数几乎是不可想象的,不过现在C++11已经支持lambda创建匿名函数了。对于一些小而简单的代码,创建匿名函数再方便不过了,因为围绕程序员最头疼的难题就是吃饭吃什么,变量、函数该取什么名,所以Lambda支持必然深受大家的喜爱。
传统上,大家都是系统先定义一个含有operator()的命名类,然后在创建该类的一个对象,最终在合适的位置通过该对象调用函数。其实,这个步骤就算是lambda的前身了,lambda语法自动创建匿名类和匿名对象,像是上面传统可调用类实现繁琐步骤的快速实现,一处定义且只使用一次,可以方便的结合各种标准算法库作为谓词使用,或者智能指针的deleter定义,同时也可以创建闭包任务执行各种回调等任务。总之结合C++可调用对象的概念,可以大大简化了项目的设计和实现风格。
1
| std::find_if(container.begin(), container.end(), [](int val) { return val > 0 && val < 10; });
|
本来以为lambda把捕获搞清楚就可以了,但是细究下去还是需要梳理一下。
lambda的组件包括:一个可能为空的捕获列表、一个可选的参数列表、一个可选的mutable修饰符、一个可选的noexcept修饰符、一个可选的->返回类型、一个执行表达式体。
1
| [ capture ] ( params ) opt -> ret { body; }
|
一、捕获
1.1 lambda的捕获类型
有些时候,我们需要控制lambda是否允许和如何访问局部名字,这时候就需要指明捕获信息。在lambda中,我们可以选择的捕获类型有下面这几种:
[]: 空捕获列表,意味着lambda无法使用期外层上下文中的任何局部名字,其内部执行体所需的符号只能从实参或者非局部变量中获取;
[&]: 引用隐式捕获,其所有的局部名字都能使用,所有的局部变量都用引用访问;
[=]: 按值隐式捕获,所有的局部名字都能使用,所有名字都是指向局部变量的副本,这些副本是在lambda表达式的调用点获得的;
[捕获列表]: 只捕获列表中的变量,不捕获其他变量,捕获列表中可以出现this。
[&, 捕获列表]、[=, 捕获列表]: 某些变量进行特殊的捕获方式,其他变量采用默认捕获方式,捕获列表中可以出现this。
在使用中,当我们考虑选择捕获类型的时候,使用捕获列表可以具有更细粒度的捕获控制,而如果希望局部对象可以写入修改,或者捕获的对象很大会有拷贝负担,则可以考虑使用引用捕获;但是lambda用于闭包执行的话,其有效期可能会超过其调用者,而且如果lambda的创建和执行在不同线程的话,一般通过按值捕获会更加安全。