免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 8098 | 回复: 13

[C++] C++ 0x新特性:详细讲解lambda表达式 [复制链接]

论坛徽章:
0
发表于 2011-07-08 08:58 |显示全部楼层
本帖最后由 chary8088 于 2011-07-08 09:19 编辑

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

Email: libo22@gmail.com


Lambda表达式和闭包,part1

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

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


为什么使用lambda表达式?

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

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

view plainprint?

   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. };  

class withinrange {
double low, high;
public:
withinrange(double l, double h) : low(l), high(h) { }
bool operator()(const employee& emp) {
  return emp.salary() >= low && emp.salary() < high;


  }
};

接下来,使用find_if算法来定位第一个工资在指定范围的员工:

view plainprint?

   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>  

double minimum_salary=1000;
std::find if(employees.begin(), employees.end(),
withinrange(minimum_salary, 1.25*  minimum_salary));


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

使用Lambda 表达式:

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

view plainprint?

   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));  

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


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


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

view plainprint?

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

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


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


隐式和显式的返回类型


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

view plainprint?

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

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


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

view plainprint?

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

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


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


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

view plainprint?

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

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


Lambda表达式函数体

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

view plainprint?

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

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


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


外部引用

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

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

view plainprint?

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

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


有外部引用的例子:

view plainprint?

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

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


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


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

第二部分翻译,请看原博客
本文转自:http://blog.csdn.net/chary8088/article/details/6586718

论坛徽章:
0
发表于 2011-07-08 09:05 |显示全部楼层
不错,学习了。

论坛徽章:
1
2015年迎新春徽章
日期:2015-03-04 09:50:28
发表于 2011-07-08 09:15 |显示全部楼层
c就要有c的样子,用什么lambda。

论坛徽章:
0
发表于 2011-07-08 09:58 |显示全部楼层
不错,c++确实该引入这个表达方法了

论坛徽章:
1
射手座
日期:2013-08-21 13:11:46
发表于 2011-07-09 14:11 |显示全部楼层
lambda看上去蛮好的,不用写那些憋足的function了

论坛徽章:
0
发表于 2011-07-09 14:56 |显示全部楼层
lz可以考虑写一个较全的C++0x语言特性和标准库的介绍,必定是精华

论坛徽章:
0
发表于 2011-07-09 16:19 |显示全部楼层

有人说未来c++会有几万种"进化"的功能,据说都是"绝活".

论坛徽章:
1
射手座
日期:2013-08-21 13:11:46
发表于 2011-07-10 13:31 |显示全部楼层
lz可以考虑写一个较全的C++0x语言特性和标准库的介绍,必定是精华
tyc611 发表于 2011-07-09 14:56


Overview of the New C++ (C++0x),已经有人写了,去下一本看吧

论坛徽章:
0
发表于 2011-07-11 10:21 |显示全部楼层
本帖最后由 chary8088 于 2011-07-11 10:22 编辑
lz可以考虑写一个较全的C++0x语言特性和标准库的介绍,必定是精华
tyc611 发表于 2011-07-09 14:56



  the part 2 is being tanslated now

论坛徽章:
3
15-16赛季CBA联赛之山东
日期:2016-10-30 08:47:3015-16赛季CBA联赛之佛山
日期:2016-12-17 00:06:31CU十四周年纪念徽章
日期:2017-12-03 01:04:02
发表于 2011-07-11 16:53 |显示全部楼层
回复 8# egmkang


    给个地址... TKS
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP