C++ 0x新特性:详细讲解lambda表达式

此文为本人的原创翻译,转载请注明作者及版权信息!!

翻译:李波

Email: libo22@gmail.com


Lambda表达式和闭包,part1

C++标准委员会在2008年2月的Bellevue会议上通过了lambda的提议,最新版本的提议与我2005年在这里展示有很大不同。

术语已经修改了,像语法,语义,使用,以及lambda的实现。在这个系列的第一部分我会介绍最新lambda表达式的概念和原理。


为什么使用lambda表达式?

一个lambda表达式(也称lambda函数)就是一个定义在调用那里的无名函数。就其本身而言,它和函数对象非常相似。的确,lambda表达式是被自动转换成函数对象的;那为什么不直接使用函数对象呢?也许lambda表达式更好用,创建函数对象是非常费劲的:有必须定义一个类和它的数据成员,一个重载函数调用operator和构造函数。然后你必须在所有调用的地方实例化那个类型的对象。这是非常繁琐的。

为了演示lambda的长处,假设你必须找到第一个员工,他的工资在给定的范围内;使用传统繁琐的函数对象,你可以写一个withinrange class:

  1. class withinrange {   
  2.  double low, high;  
  3. public:  
  4.  withinrange(double l, double h) : low(l), high(h) { }  
  5.  bool operator()(const employee& emp) {   
  6.   return emp.salary() >= low && emp.salary() < high;  
  7.   
  8.   
  9.   }  
  10. };  
接下来,使用find_if算法来定位第一个工资在指定范围的员工:

  1. <span style="font-size: 16px; ">double minimum_salary=1000;  
  2. std::find if(employees.begin(), employees.end(),  
  3. withinrange(minimum_salary, 1.25*  minimum_salary));</span>  

find_if第三个参数是一个函数对象,在其他语言叫做闭包(closure).闭包是一个存储被调用函数函数环境的无名函数对象,环境由调用函数访问的局部变量组成;在这个例子中,数据成员low 和 high 是存储在闭包中的环境;用更简洁的话说,一个闭包就是一个由编译器根据lambda表达式生成的假设的函数对象;

使用Lambda 表达式:

使用新的lambda表达式,上面的find_if可以这样重写:

  1. double minimum_salary = 1000;  
  2. double upper_limit = 1.25 * minimum_salary;  
  3. std::find if(employees.begin(), employees.end(),  
  4. [&](const employee& emp)  (emp.salary() >= minimum_salary && emp.salary() < upper_limit));  

首先,注意 在一行代码里你就写 l ambda表达式,不再需要另外定义一个函数。


Lambda表达式用[]作为开始的标志(我会在以后的系列中讨论[]中&的含义),[]后面是lambda表达式的参数列表,在这个例子中,参数列表有唯一的参数const employee&组成,整个lambda表达式可以说是单一的,因为它的参数列表被显式指定的,这里,单一的参数类型是const employee&,同样的lambda表达式多态版本可以这样写:

  1. [&](emp) (emp.salary() >= minimum_salary && emp.salary() < upper_limit)  

这种写法要求参数类型应该从上下文推导出来(调用的地方);当前的标准集中在单一类型的lambda表达式,所以,在这些章节里我不会讨论多态类型的lambda表达式。


隐式和显式的返回类型


前一个lambda表达式的最后一部分:

  1. (emp.salary() >= minimum_salary && emp.salary() < upper_limit)  

这是一个lambda表达式的函数体部分。一个lambda表达式由一对括号组成。在那个例子中,lambda函数的返回类型是从表达式本身隐式推导出来的;例如,下面的表达式产生一个bool类型结果:

  1. (emp.salary() >= minimum_salary && emp.salary() < upper_limit)  


因此,上面lambda表达式的返回类型是bool类型。


从技术上讲,如果返回类型没有显式的指定,返回类型被定义为decltype(e),e 是在函数体内调用的;但你也可以显示的指定lambda表达式的返回类型;借助新的函数声明语法,我们可以这样写:

  1. [&](emp) ->bool (emp.salary() >= minimum_salary && emp.salary() < upper_limit)  

Lambda表达式函数体

一个lambda表达式的函数体可以包含多条语句,在这种情况下,函数体由一对大括号包住(和我们普通的函数体一样)并且必须有显式的返回语句;下面的lambda表达式有两个int类型参数和一个int类型的返回值,函数体有有三条语句组成和大括号组成:

  1. [](int x, int y) -> int { int z; z = x + y; return z; }  

返回语句在这里是必须的因为lambda表达式有多条语句组成,同样地,参数列表后的显示返回类型也是必须指定的。


外部引用

lambda表达式被分为两个大的种类:无外部引用和有外部引用,后者(外部引用)用来访问定义在lambd表达式参数列表外的变量,相反的,无外部引用的表达式不访问定义在lambd表达式参数列表外的变量;

无外部引用的lambda表达式的例子:

  1. [](int x, int y) -> int { return x + y; }  

有外部引用的例子:

  1. int z;  
  2. myfunc([](int x, int y) -> int { return x + y + z; } );//pseudo code  

引用定义在lambda表达式外的局部变量已经争论了很长时间了,问题是在lambda函数体内引用的局部变量必须以某种方式保存在结果闭包里,比如前面的这个例子;这些变量是怎样保存在闭包里是争议的问题;一些人建议使用外部变量的拷贝保存在闭包里,然后拷贝在一些情况下是低效的,并有可能导致变量切片(译者注:比如类)和迭代器失效,其他方案建议保存这些变量的引用,这种方法也可以是静态的因为它可能导致悬挂引用。在这个系列第二个部分我会show最新的方案怎么样解决外部引用问题和外部引用在闭包怎样表现的。


欢迎大家发表自己的看法和见解,我们一起讨论,文中有不足之处,也请指出,多谢了

第二部分正在翻译,待续。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值