C++ 新特性 lambda表达式与匿名函数

lambda 表达式

Lambda 表达式 是一个源自阿隆佐•丘奇(Alonzo Church) -- 艾伦•图灵(Alan Turing)的老师--的术语。
丘奇创立了 \(\lambda\) 演算,后来被证明和图灵机是等价的。
Lambda 表达式是 C++11 中最重要的新特性之一,而 Lambda 表达式就是提供了一个类似匿名函数的特性,而匿名函数则是在需要一个函数,但是又不想费力去命名一个函数的情况下去使用的。这样的场景很多,所以匿名函数几乎是现代编程语言的标配。

1. Lambda 表达式基础

Lambda 表达式的基本语法如下:

[捕获列表]() mutable(可选) 异常属性 -> 返回类型 {
    // 函数体
};
[ caputrue ] ( params ) opt -> ret { body; };
  • Lambda 表达式以一对中括号开始
  • 跟函数定义一样,我们有参数列表
  • 跟正常的函数定义一样,我们会有一个函数体,里面会有return 语句
  • Lambda 表达式一般不需要说明返回值(相当于 auto);有特殊情况需要说明时,则应使用箭头语法的方式
  • 每个 Lambda表达式都有一个全局唯一的类型,要精确捕捉 Lambda 表达式到一个变量中,只能通过 auto 声明的方式

基本使用

  • 参数列表
  • 返回类型
  • 函数体
#include <iostream>

using namespace std;

int Foo(int a, int b) {
    return a + b;
}

int main() {
    // lambda 表达式就是匿名函数(没有名字的函数)
    // []捕获列表 ()参数列表 -> 返回值
    int c = [](int a, int b) -> int {
        // 函数体
        return a + b;
    }(1, 2);

    cout << c << endl;

    auto f = [](int a, int b) -> int {
        // 函数体
        return a + b;
    };

    c = f(1, 2);

    cout << c << endl;

    // 函数式编程 多线程,并发
    int d = [](int n) {
       return [n](int x) {
            return n + x;
        }(1);
    }(2);

    cout << d << endl;

    auto fun = [](int n) {
       return [n](int x) {
            return n + x;
        };
    };

    c = fun(1)(2);

    cout << c << endl;

}

2. mutable

#include <iostream>

using namespace std;

int main() {
    int t = 10;
    auto f = [t]() {
        return ++t; // 报错,"t": 无法在非可变 lambda 中修改通过复制捕获
    };
}
#include <iostream>

using namespace std;

int main() {
    int t = 10;
    // 按值捕获
    auto f = [t]() mutable{
        return ++t; // t 拷贝了一份,独一无二
    };

    cout << f() << endl; // 11
    cout << f() << endl; // 12
    cout << t << endl; // 10
}
#include <iostream>

using namespace std;

int main() {
    int t = 10;
    // 按值捕获
    auto f = [t]() mutable{
        return ++t; // t 拷贝了一份,独一无二
    };

    auto f2 = [t]() mutable{
    return ++t; // t 拷贝了一份,独一无二
    };

    cout << f() << endl; // 11
    cout << f2() << endl; // 11
    cout << f() << endl; // 12
    cout << f2() << endl; // 12
    cout << t << endl; // 10
}

3. 捕获列表

所谓捕获列表,其实可以理解为参数的一种类型,lambda 表达式内部函数体在默认情况下是不能够使用函数体外部变量的,这时候捕获列表可以起到传递外部参数的作用。根据传递的行为,捕获列表也分为以下几种:

1. 值捕获

与参数传值类似,值捕获的前提是变量可拷贝,不同之处在于,被捕获的变量在 lambda 表达式被创建时拷贝,而非调用时才拷贝:

void learn_lambda_func_1() {
    int value_1 = 1;
    auto copy_value_1 = [value_1]() {
        return value_1;
    };

    value_1 = 100;
    auto stored_value_1 = copy_value_1();
    // 这时,stored_value_1 == 1, 而 value_1 == 100
    // 因为 copy_value_1在创建时就保存了一份 value_1 的拷贝
    cout << "value_1 = " << value_1 << endl;
    cout << "stored_value_1 = " << stored_value_1 << endl;
}
2. 引用捕获

与引用传参类似,引用捕获保存的是引用,值会发生变化

#include <iostream>

using namespace std;

int main() {
    int t = 10;

    // 按值捕获, 捕获的是声明匿名函数时,捕获列表的参数的值
    // auto f = [t]() {
    //     cout << t << endl; 
    // };

    // f();

    // 按引用捕获
    auto f2 = [&t]() {
        cout << t << endl;
        t = 13;
    };

    t = 11;

    f2(); // 11

    cout << t << endl; // 13

    // 捕获列表
    int a = 1;
    int b = 2;
    int c = 3;
    int d = 4;
    [a, b]() {
        cout << a << " + " << b << endl;
    }();

    // 捕获所有的变量 按值捕获
    [=]() {
        cout << a << " + " << b << endl;
    }();

    // 按引用捕获所有引用
    [&]() {
        cout << a++ << " + " << b++ << endl;
    }();

    // 按照值和引用的方式捕获变量
    [a,b,&c,&d]() {
        cout << a << "+" << b << endl;
        cout << c++ << " + " << d++ << endl;
    }();
}
3. 隐式捕获

手动书写捕获列表有时候是非常复杂的,这种机械性的工作可以交给编译器来处理,这时候可以在捕获列表中写一个 &= 向编译器声明采用 引用捕获或者值捕获
总结一下,捕获提供了 lambda 表达式对外部值进行使用的功能,捕获列表的最常用的四种形式是

  • [] 空捕获列表
  • [name1, name2, ...] 捕获一系列变量
  • [&] 引用捕获,让编译器自行推导捕获列表
  • [=] 值捕获,让编译器推导应用列表
4. 使用案例
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main() {
    vector<int> v = { 1, 2, 3, 4, 5 };

    for (int i = 0; i < v.size(); ++i) {
        if (v[i] % 2 == 0) {
            cout << v[i] << "是偶数" << endl;
        } else {
            cout << v[i] << "是奇数" << endl;
        }
    }

    // 高阶写法
    for_each(v.begin(), v.end(), [](int n) {
        if (n % 2 == 0) {
            cout << n << "是偶数" << endl;
        } else {
            cout << n << "是奇数" << endl;
        }
    });
}
posted @ 2022-09-14 21:08  岁月飞扬  阅读(85)  评论(0编辑  收藏  举报