lambda表达式

lambda表达式

匿名函数(lambda函数)

匿名函数(英文名:lambda)就是没有名字的函数。最简单的匿名函数是[]{},它没有参数也没有返回值。在匿名函数中,[]里面用来捕获函数外部的变量,而()里面就是匿名函数的参数,{}里面就是函数的执行代码。

匿名函数也称lambda函数lambda表达式

示例:

#include <iostream>
using namespace std;
int main()
{
    //定义函数,使用auto来自动获取function的类型
    auto function = [](){ cout << "Hello world!"; };	
    fuction();	//调用函数
    return 0;
}

lambda表达式的声明

lambda表达式完整声明形式如下:

[capture list](params list)mutable exception->return type{function body}

具体含义如下:

  • capture list:捕获外部变量列表,总是出现在lambda表达式的起始位置,编译器根据[]来判断接下来的代码是否为lambda表达式

  • params list:形参列表

  • mutable:指示符,用来表示是否可修改捕获的变量,使用mutable指示符时,参数列表不能省略,即使为空

  • exception:异常设定,用于指定函数抛出的异常,如抛出整数类型的异常,可以使用 throw(int)

  • return type:返回类型,返回类型可以省略,但编译器会推断出lambda表达式的返回类型:(1)如果function body中存在return语句,则表达式的返回类型由return语句的返回类型决定;(2)如果function body中没有return语句,则返回类型为void

  • function body:函数主体

    同时,也可以省略其中的某些成分来声明“不完整”的lambda表达式
    常见的几种如下:

1. [capture list](params list)->return type{function body}
2. [capture list](params list){funtion body}
3. [capture list]{fuction body}

实例:

#include <iostream>
#include <vector>
#include <time.h>
#include <algorithm>
using namespace std;

int main()
{
    srand(time(0));
    vector<vector<int>> nums(10, vector<int>(2, 0));
    for (int i = 0; i < nums.size(); i++)
    {
        nums[i][0] = i;
        nums[i][1] = rand() % 10;
    }
    sort(nums.begin(), nums.end(), [](vector<int> &a, vector<int> &b) -> bool
         { return a[1] < b[1]; });
    for (vector<int> &num : nums)
    {
        cout << num[0] << '(' << num[1] << ") ";
    }
    return 0;
}
//结果
//4(2) 5(2) 9(2) 0(5) 8(5) 1(7) 3(7) 7(8) 2(9) 6(9)

使用STL中的sort函数时,可以为其提供一个谓词函数作为排序的依据,这里使用lambda表达式作为谓词函数,方便简洁

lambda表达式具体用法

下面对lambda表达式中各项的具体用法进行介绍

捕获外部变量

lambda表达式可以使用其可见范围内的外部变量,但必须声明哪些外部变量可以被该lambda表达式使用。lambda表达式通过在最前面的方括号[] 来指明内部可以访问的外部变量,这一过程也称lambda表达式“捕获了”外部变量

  • 捕获列表可由多个捕获项组成,以逗号隔开,需要注意捕获项不能重复,否则会编译错误。比如[=,num],=已经捕获过num了
  • 只能捕获当前作用域的局部变量,捕获作用域外的局部变量或非局部变量都会报错
  • lambda表达式之间不能赋值

类似参数传递方式(值传递、引用传递、指针传递),在lambda表达式中,外部变量的捕获方式也有值捕获引用捕获隐式捕获

1. 值捕获

值捕获与参数传递中的值传递类似,被捕获的变量在lambda表达式创建时通过值拷贝的方式传入,随后对该变量的修改不会影响lambda表达式

需要注意,以值捕获外部变量,在lambda表达式函数体中不能对该外部变量的值进行修改,即值捕获具有常性

示例:

#include <iostream>
using namespace std;

int main()
{
    int num = 17;
    auto func = [num]{cout << num << endl;};
    num = 19;
    func();
    return 0;
}
//结果
//17

这个例子中lambda表达式在创建时捕获了num变量,并在创建后在外部对num的值进行了修改,在使用lambda表达式时输出的仍是num的初始值,体现了lambda表达式是在创建时获得了num变量的值

2. 引用捕获

使用引用捕获一个外部变量,只需在捕获变量前加上一个引用说明符&,引用捕获不具有常性

示例:

#include <iostream>
using namespace std;

int main()
{
    int num = 17;
    auto func = [&num]{cout << num << endl;};
    num = 19;
    func();
    return 0;
}
//结果
//19

3. 隐式捕获

值捕获和引用捕获都需要我们在捕获列表中显示地列出lambda表达式中使用的外部变量。

此外,我们还可以让编译器根据函数体中的代码来推断需要捕获哪些外部变量,这种方式称为隐式捕获。隐式捕获有两种方式,分别是[=]和[&],[=]表示以值捕获,[&]表示以引用捕获

隐式值捕获示例:

#include <iostream>
using namespace std;

int main()
{
    int num1 = 17 , num2 = 21;
    auto func = [=]{cout << num1 << ',' << num2 << endl;};
    num1 = 19;
    num2 = 27;
    func();
    return 0;
}
//结果
//17,21

隐式引用捕获示例:

#include <iostream>
using namespace std;

int main()
{
    int num1 = 17 , num2 = 21;
    auto func = [&]{cout << num1 << ',' << num2 << endl;};
    num1 = 19;
    num2 = 27;
    func();
    return 0;
}
//结果
//19,27

4. 混合方式

上面的例子,要么是值捕获,要么是引用捕获,Lambda表达式还支持混合的方式捕获外部变量,这种方式主要是以上几种捕获方式的组合使用。

示例:

#include <iostream>
using namespace std;
#define p 12
int main()
{
    int num1 = 17 , num2 = 21;
    auto func = [num1,&num2](){cout << num1 << ',' << num2 << endl;};
    num1 = 19;
    num2 = 27;
    func();
    return 0;
}
//结果
//17,27

C++11中的lambda表达式捕获外部变量的主要形式

捕获形式 具体说明
[] 不捕获任何外部变量
[变量名,...] 默认以值捕获指定的多个变量,如果引用捕获,需显示声明(&)
[this] 以值捕获this指针
[=] 以值捕获所有需要的外部变量,包括this指针
[&] 以引用捕获所有需要的外部变量,包括this指针
[=,&num1,&num2] 以引用捕获变量num1、num2,其余变量以值捕获
[&,num1,num2] 以值捕获变量num1、num2,其余变量以引用捕获

mutable指示符——修改捕获变量

前面有提到,在lambda表达式中,如果以值传递捕获外部变量,则函数体中不能修改该外部变量,否则会编译错误

通过使用mutable关键字,用以说明表达式的函数体可以修改值捕获的变量

示例:

#include <iostream>
using namespace std;

int main()
{
    int num1 = 17 , num2 = 21;
    auto func = [=]()mutable{cout << ++num1 << ',' << ++num2 << endl;};
    num1 = 19;
    num2 = 27;
    func();
    return 0;
}
//结果
//18,22

lambda表达式的参数列表

lambda表达式的参数与普通函数的参数类似,但在lambda表达式中传递参数存在着一些限制,主要有以下几点

  • 参数列表中不能有默认参数
  • 不支持可变参数
  • 所有参数必须有参数名

lambda表达式的原理

实际上编译器在全局作用域自动生成了一个类,在类中重载了operator(),operator()函数的内容就是lambda表达式的内容。

故可以理解为lambda表达式本质上还是仿函数,是一个编译器自动生成的仿函数。

lambda表达式与STL

lambda表达式的引入为STL的使用带来了极大的便利,我们可以在泛型算法中提供lambda表达式作为谓词函数进行某些操作

示例(统计元素和):

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

int main()
{
    vector<int> nums{1, 2, 3, 4, 5, 6, 7, 8, 9};
    int sum = 0;
    for_each(nums.begin(), nums.end(), [&sum](int val)
             { sum += val; });
    cout << sum << endl;
    return 0;
}
//结果
//45
posted @ 2023-01-18 12:52  Vergissmeinnicht_z  阅读(150)  评论(0编辑  收藏  举报