C++11新特性——lambda函数

此课件及源代码来自B站up主:码农论坛,该文章仅作为本人学习笔记使用。

lambda函数是C++11标准新增的语法糖,也称为lambda表达式或匿名函数。

lambda函数的特点是:距离近、简洁、高效和功能强大。

示例:[](const int& no) -> void { cout << "亲爱的" << no << "号:我是一只傻傻鸟。\n"; };

语法:

     

示例:

#include <iostream>
#include <vector>
#include <algorithm>
using  namespace std;

// 普通函数。
void zsshow(const int & no)  { 
      cout << "亲爱的" << no << "号:我是一只傻傻鸟。\n";
}
// 仿函数。
class czs  
{
public:
      void operator()(const int & no) {
            cout << "亲爱的" << no << "号:我是一只傻傻鸟。\n";
      }
};

int main()
{
      vector<int> vv = { 5,8,3 };
      // 第三个参数是普通函数。
      for_each(vv.begin(), vv.end(), zsshow); 
      // 第三个参数是仿函数。
      for_each(vv.begin(), vv.end(), czs());      
      // 第三个参数是lambda表达式。
      for_each(vv.begin(), vv.end(),
            [](const int& no) {
                 cout << "亲爱的" << no << "号:我是一只傻傻鸟。\n";
            }
      );
}

一、参数列表

参数列表是可选的,类似普通函数的参数列表,如果没有参数列表,()可以省略不写。

与普通函数的不同:

  1. lambda函数不能有默认参数。
  2. 所有参数必须有参数名。
  3. 不支持可变参数。

二、返回类型

用后置的方法书写返回类型,类似于普通函数的返回类型,如果不写返回类型,编译器会根据函数体中的代码推断出来。

如果有返回类型,建议显式的指定,自动推断可能与预期不一致。

三、函数体

类似于普通函数的函数体。

四、捕获列表

通过捕获列表,lambda函数可以访问父作用域中的非静态局部变量(静态局部变量可以直接访问,不能访问全局变量)。

捕获列表书写在[]中,与函数参数的传递类似,捕获方式可以是值和引用。

以下列出了不同的捕获列表的方式。

1)值捕获

与传递参数类似,采用值捕获的前提是变量可以拷贝。

与传递参数不同,变量的值是在lambda函数创建时拷贝,而不是调用时拷贝。

例如:

      size_t v1 = 42;
      auto f = [ v1 ]  { return v1; };   // 使用了值捕获,将v1拷贝到名为f的可调用对象。
      v1 = 0;
      auto j = f();    // j为42,f保存了我们创建它是v1的拷贝。

由于被捕获的值是在lambda函数创建时拷贝,因此在随后对其修改不会影响到lambda内部的值。

默认情况下,如果以传值方式捕获变量,则在lambda函数中不能修改变量的值。

2)引用捕获

和函数引用参数一样,引用变量的值在lambda函数体中改变时,将影响被引用的对象。

      size_t v1 = 42;
      auto f = [ &v1 ]  { return v1; }; // 引用捕获,将v1拷贝到名为f的可调用对象。
      v1 = 0;
      auto j = f();    // j为0。

如果采用引用方式捕获变量,就必须保证被引用的对象在lambda执行的时候是存在的。

3)隐式捕获

除了显式列出我们希望使用的父作域的变量之外,还可以让编译器根据函数体中的代码来推断需要捕获哪些变量,这种方式称之为隐式捕获。

隐式捕获有两种方式,分别是[=]和[&]。[=]表示以值捕获的方式捕获外部变量,[&]表示以引用捕获的方式捕获外部变量。

    int a = 123;
    auto f = [ = ]  { cout << a << endl; };       //值捕获
    f();       // 输出:123
    auto f1 = [ & ] { cout << a++ << endl; };       //引用捕获
    f1();     //输出:123(采用了后++)
    cout << a << endl;          //输出 124

4)混合方式捕获

lambda函数还支持混合方式捕获,即同时使用显式捕获和隐式捕获。

混合捕获时,捕获列表中的第一个元素必须是 = 或 &,此符号指定了默认捕获的方式是值捕获或引用捕获。

需要注意的是:显式捕获的变量必须使用和默认捕获不同的方式捕获。例如:

      int i = 10;
      int  j = 20;
      auto f1 = [ =, &i] () { return j + i; };       // 正确,默认值捕获,显式是引用捕获
      auto f2 = [ =, i] () { return i + j; };         // 编译出错,默认值捕获,显式值捕获,冲突了
      auto f3 = [ &, &i] () { return i +j; };       // 编译出错,默认引用捕获,显式引用捕获,冲突了

5)修改值捕获变量的值

lambda函数中,如果以传值方式捕获变量,则函数体中不能修改该变量,否则会引发编译错误。

lambda函数中,如果希望修改值捕获变量的值,可以加mutable选项,但是,在lambda函数的外部,变量的值不会被修改。

    int a = 123;
    auto f = [a]()mutable { cout << ++a << endl; }; // 不会报错
    cout << a << endl;     // 输出:123
    f();                              // 输出:124
    cout << a << endl;     // 输出:123

6)异常说明

lambda可以抛出异常,用throw(…)指示异常的类型,用noexcept指示不抛出任何异常。

五、lambda函数的本质

当我们编写了一个lambda函数之后,编译器将它翻译成一个类,该类中有一个重载了()的函数。

1)采用值捕获

采用值捕获时,lambda函数生成的类用捕获变量的值初始化自己的成员变量。

例如:

int a =10;
int b = 20;
auto addfun = [=] (const int c ) -> int { return a+c; };
int c = addfun(b);   
cout << c << endl;

等同于:

class Myclass
{
      int m_a;           // 该成员变量对应通过值捕获的变量。
public:
      Myclass( int a ) : m_a(a){};  // 该形参对应捕获的变量。
      // 重载了()运算符的函数,返回类型、形参和函数体都与lambda函数一致。
      int operator()(const int c) const
      {
            return a + c;
      }
};

默认情况下,由lambda函数生成的类是const成员函数,所以变量的值不能修改。如果加上mutable,相当于去掉const。这样上面的限制就能讲通了。

2)采用引用捕获

如果lambda函数采用引用捕获的方式,编译器直接引用就行了。

唯一需要注意的是,lambda函数执行时,程序必须保证引用的对象有效。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值