C++ Lambda表达式

 

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表达式的类型。 其他情况,必须显式定义一个返回类型。