zoukankan      html  css  js  c++  java
  • 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:

    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算法来定位第一个工资在指定范围的员工:

    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可以这样重写:

    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表达式多态版本可以这样写:

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

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


    隐式和显式的返回类型


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

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

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

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


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


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

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

    Lambda表达式函数体

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

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

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


    外部引用

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

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

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

    有外部引用的例子:

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

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


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

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

  • 相关阅读:
    vagrant 入门3
    vagrant 入门4
    vagrant 入门2
    Map、Debug追踪
    Comparator比较器 、Comparable接口
    File类
    Lambda表达式、函数式编程思想概述
    异常--异常体系、异常的处理、异常的捕获、finally语句块和自定义异常
    List集合、Set集合、Collection集合工具类
    数据结构---栈、队列、数组、链表和红黑树
  • 原文地址:https://www.cnblogs.com/mtcnn/p/9410140.html
Copyright © 2011-2022 走看看