lambda表达式随记,都是来源于网络上的学习。后续自己用到了会加新东西的。

1 介绍

基本语法:

[capture list] (parameter list) -> return type { function body }

1 capture list 是捕获列表,用于指定 Lambda表达式可以访问的外部变量,分为按值和按引用的方式访问。捕获列表可以为空,表示不访问任何外部变量,也可以使用默认捕获模式 & 或 = 来表示按引用或按值捕获所有外部变量,还可以混合使用具体的变量名和默认捕获模式来指定不同的捕获方式。

例如 :

这些都是在函数中的,只不过为了理解,就省略了外面的函数了。
    
例1 :现在是最简单直接的捕获,适用于你只需要几个外部变量的时刻.(这样是显示的捕获外部变量)
int x = 100;
int y = 100;
[x,&y](int m) {m = x+y;
              	x = 50;
               	y = 1;
              }
其中对于 x 是值捕获,对于y是引用捕获
   这个应该是好理解的,具体就像传参时一样呀,按值传就是形不改实;
按引用传就是需要形改实的场景了。
    执行完就是x = 100,y = 1。了
    
例2:使用于你需要在lambda表达式中访问很多外部变量,并且其中有几个你需要更改它值。(隐式捕获外部变量)
1:   int x1,.....x6;(都初始化为6)
	[=,&x2](){int m = x1;		//隐式按值捕获所有的外部变量,对于x2显示按引用捕获。
             	x1 = 10;
             	x2 = 2;
             }

2:int x1,.....x6;(都初始化为6)
	[=](){int m = x1;		//隐式按值捕获所有的外部变量。
             	x1 = 10;
             	x2 = 2;
             }    

3:int x1,.....x6;(都初始化为6)
	[&,=x2](){int m = x1;		//隐式按引用捕获所有的外部变量,对于x2显示按值捕获。
             	x1 = 10;
             	x2 = 2;
             }

4:3:int x1,.....x6;(都初始化为6)
	[&](){int m = x1;		//隐式按引用捕获所有的外部变量。
             	x1 = 10;
             	x2 = 2;
             }
这样都是可以的。但是不能同类型的捕获方式再显示捕获一次。
    int x1,.....x6;(都初始化为6)
	[=,=x2](){int m = x1;		//不可以的。同理隐式引用捕获也是不行
             	x1 = 10;
             	x2 = 2;
             }

例3:初始化捕获,c++14引入的一种新的捕获方式,它允许在捕获列表中使用初始化表达式,从而在捕获列表中创建并初始化一个新的变量,而不是捕获一个已存在的变量。这种方式可以使用 auto 关键字来推导类型,也可以显式指定类型。这种方式可以用来捕获只移动的变量,或者捕获 this 指针的值。
    int x = 10;
	auto f = [z = x + 5] (int y) -> int { return z + y; }; // 初始化捕获 z,相当于值捕获 x + 5
	x = 20; // 修改外部的 x
	cout << f(5) << endl; // 输出 20,不受外部 x 的影响
这个初始化捕获是复制别人的,我基本用不到。也没有尝试过。

2 parameter list 是参数列表,用于表示 Lambda表达式的参数,可以为空,表示没有参数,也可以和普通函数一样指定参数的类型和名称,还可以在 c++14 中使用 auto 关键字来实现泛型参数。

3 return type 是返回值类型,用于指定 Lambda表达式的返回值类型,可以省略,表示由编译器根据函数体推导,也可以使用 -> 符号显式指定,还可以在 c++14 中使用 auto 关键字来实现泛型返回值。

4 function body 是函数体,用于表示 Lambda表达式的具体逻辑,可以是一条语句,也可以是多条语句,还可以在 c++14 中使用 constexpr 来实现编译期计算。

使用场景

1 作为回调函数/函数参数

std::vector<int> nums = {3, 1, 4, 1, 5};
int threshold = 3;

// 排序:用lambda定义降序规则(无需单独写一个比较函数)
std::sort(nums.begin(), nums.end(), [](int a, int b) {
    return a > b; // 降序排列
});
    
// 筛选出大于threshold的元素(捕获外部变量threshold)
std::vector<int> result;
std::for_each(nums.begin(), nums.end(), [&result, threshold](int num) {
    if (num > threshold) {
        result.push_back(num); // 用&捕获result(引用传递,可修改)
    }
});

2 多线程中的任务定义

在多线程编程中,lambda 可以快速定义线程要执行的任务,避免为简单逻辑单独定义函数。

int count = 0;
    
// 启动线程,用lambda定义线程任务(捕获并修改count)
std::thread t([&count]() {
    for (int i = 0; i < 1000; ++i) {
        count++; // 访问外部变量count
    }
});

Lambda 表达式的最佳场景是:短期使用、逻辑简单、需要访问上下文变量的函数。

lambda表达式本质

lambda本质上也是一种函数对象,重载了operator()的类的对象。每一个lambda表达式都对应一个唯一的匿名类。这个类由编译器自动生成,因此我们无法直接获取。

lambda表达式的:捕获列表实际上是匿名类的数据成员;它的参数列表实际上是匿名类的operator()方法的参数列表;返回值类型实际上是这个匿名类的 operator()方法的返回值类型;函数体实际上是匿名类的operator()方法的函数体。

对于这段话的理解:
1 首先需要明白什么是 函数对象。

// 1. 定义一个重载了operator()的类
class Add {
public:
    // 重载operator(),接收两个int,返回int
    int operator()(int a, int b) const {
        return a + b;
    }
};

int main() {
    // 2. 创建该类的对象(函数对象)
    Add add_obj;
    
    // 3. 像调用函数一样使用对象:本质是调用 add_obj.operator()(3,5)
    int result = add_obj(3, 5); // 结果为8
    return 0;
}

解释:Add add_obj; 实际上add_obj还是一个类对象。
为什么也把它叫函数对象呢?
因为Add类中重载了operator()方法。于是我们就可以像使用函数一样,执行 add_obj(3,5),给定参数就好了,我们在代码中调用定义好的函数时不也是这样吗。因为他的行为很像函数调用,所以称它为 “函数对象”。
本质是调用 add_obj.operator()(3,5);

我们的lambda表达式就是这样一个逻辑

编译器视角 ——我们写的Lambda表达式 被 “翻译” 成了什么?

// 编译器自动生成的匿名类(用户不可见)
class __Lambda_123 {
private:
    // 对应Lambda的捕获列表:数据成员x_
    int x_; 
    int& x_;//引用捕获时

public:
    // 对应Lambda的捕获逻辑:构造函数初始化数据成员
    __Lambda_123(int x) : x_(x) {} 

    // 对应Lambda的参数列表、返回值、函数体
    int operator()(int y) const { 
        return x_ + y; // Lambda函数体被直接复制过来
    }
    //注意这里是加了const修饰的。
};

如果是在使用lambda表达式时,用了mutable修饰。例:[x] (int y){ x_= x_ + y; }mutable;

此时编译器生成的lambda表达式的匿名类为:

// 编译器自动生成的匿名类(用户不可见)
class __Lambda_123 {
private:
    // 对应Lambda的捕获列表:数据成员x_
    int x_; 

public:
    // 对应Lambda的捕获逻辑:构造函数初始化数据成员
    __Lambda_123(int x) : x_(x) {} 

    // 对应Lambda的参数列表、返回值、函数体
    int operator()(int y) { 
       x_= x_ + y; // Lambda函数体被直接复制过来
    }
    //注意这里是没有const修饰的。
};

lambda表达式是一种 “语法糖”。

“语法糖” 的核心定义是:不改变语言本质,只简化代码写法。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐