《C++进阶之C++11》【lambda表达式 + 包装器】
- lambda 是 C++ 里随写随用的 “临时函数”,不用单独定义函数名,写在遍历排序、过滤等场景里很轻便,但类型隐蔽,没法直接传参或复用 ╮(╯▽╰)╭
- 包装器(比如 function)就是 “收纳盒”,能把 lambda “装起来” 赋予明确类型,不管传参还是存起来反复用都能搞定,就像临时扳手放进工具盒,好拿好存不麻烦╰(✧∇✧)╯
简单说,lambda 负责 “快速造工具”,包装器负责 “好好存工具”,两者搭在一起,既能享受 lambda 的便捷,又能解决它的使用局限,写代码时灵活度和实用性直接拉满~ദ്ദി˶>ω<)✧
------------ lambda表达式 ------------
1. 什么是lambda表达式?
lambda表达式:是一种匿名函数,也就是没有名字的函数。
- 它是一种简洁的函数定义方式,不需要显式命名函数
- 它可以方便地定义
简短的可调用对象,用于各种需要函数对象的场景 - 可定义在
函数内部(普通函数只能定义在全局、命名空间或类中) - 语法层面无显式类型,需通过
auto或模板参数接收其匿名类型
主要特点:
- 匿名性:没有函数名
- 简洁性:通常用于简单操作,可以在一行内完成
- 临时性:常用于一次性使用或作为参数传递给高阶函数
2. lambda表达式的基本语法形式?
基本语法形式:
代码语言:javascript
AI代码解释
[捕获列表](参数列表) -> 返回类型
{
函数体
}
[capture-list] (parameters) -> return-type
{
function-body
}
- 捕获列表(
capture-list):用于指定在 lambda 表达式中可以访问的外部变量,是 lambda 能够访问所在作用域中变量的关键。- 必须出现在 lambda 最开头,编译器通过
[]识别 lambda 开始 - 它可以为空,表示不捕获任何外部变量,但是
[]不能省略(空捕获列表是语法要求)
- 必须出现在 lambda 最开头,编译器通过
- 参数列表(
parameters):和普通函数的参数列表类似,用于指定 lambda 函数的参数。- 它可以为空
- 若无需参数传递,可连同
()一起省略(如:无参 lambda 可写[] { ... })
- 返回值类型(
return-type):指定 lambda 函数的返回值类型。- 无返回值时(如:
void),可直接省略 - 返回值类型明确时(如:单一
return语句),编译器可自动推导,也可省略
- 无返回值时(如:
- 函数体(
function-body):定义 lambda 函数的具体操作和逻辑,也就是函数执行时要做的事情。- 除参数外,可直接使用捕获列表中的变量
- 即使函数体为空(如:
[] {}),{}也不能省略
通过上面的关于lambda的语法的介绍,现在我们可以明白空lambda的形式了:
代码语言:javascript
AI代码解释
// 空lambda(所有可省略部分均省略)
auto emptyLambda = [] {}; // 合法(但无实际逻辑)
2.1:捕获列表
lambda表达式默认仅能访问自身函数体、参数列表里的变量。若需使用外层作用域(如:包含 lambda 的函数作用域)的变量,需通过 捕获列表 显式声明。
捕获列表决定了 lambda 表达式对外部变量的访问方式,常见的捕获方式有以下几种: 显示捕获:在捕获列表中明确写出变量名,用逗号分隔多个变量,同时通过符号区分捕获方式:
- 值捕获:直接写变量名(如:
[x, y]) - 引用捕获:变量名前加
&(如:[&z])
隐式捕获:无需逐个写变量名,用符号批量指定捕获规则,编译器会自动识别 lambda 内用到的外层变量并捕获:
- 按值隐式捕获:捕获列表写
[=],lambda 内用到的外层变量全部传值捕获(创建副本) - 按引用隐式捕获:捕获列表写
[&],lambda 内用到的外层变量全部传引用捕获(使用引用)
混合捕获:同时用隐式捕获符号(= 或 &)和显式变量。
值捕获:在 lambda 内部对捕获变量的修改不会影响外部的原始变量。
- 例如:
[a]表示按值捕获变量a
代码语言:javascript
AI代码解释
#include <iostream>
using namespace std;
int main()
{
//1.定义一个局部变量,初始值为10
int num = 10;
//2.定义一个lambda表达式,通过值捕获方式获取外部变量num
auto lambda = [num]()mutable
{
//2.1:尝试修改捕获的副本(注意:修改的是副本,不是原始变量)
num = 20; //注意:此操作不会影响外部的num变量
//2.2:输出捕获副本的值(此时副本已被修改为20)
cout << num << endl; // 输出: 20
};
/* 注意事项:默认情况下,按值捕获的变量在lambda表达式内部是不可修改的(const)
*
* 1. 当你使用值捕获 [num] 时,lambda 内部得到的是 num 的一个副本,
* 2. 但这个副本默认是 const 的,不能修改。
* 3. 因此当你尝试在 lambda 内部修改 num 时,编译器会报错。
* 4. 要使按值捕获的变量可以在 lambda 内部修改,需要使用 mutable 关键字
*/
//3.调用lambda表达式,执行内部逻辑
lambda();
//4.输出外部num的值(未被lambda内部修改)
cout << num << endl; // 输出: 10
return 0;
}

在这里插入图片描述
引用捕获:在 lambda 内部对捕获变量的修改会直接影响外部的原始变量。
- 例如:
[&a],表示按引用捕获变量a
代码语言:javascript
AI代码解释
#include <iostream>
int main()
{
//1.定义一个局部变量 num
int num = 10;
//2.定义一个lambda表达式,使用引用捕获的方式捕获外部变量 num
auto lambda = [&num]()
{
//2.1:由于是引用捕获,这里对 num 的修改会直接反映到外部的 num 变量上
num = 20;
//2.2:输出修改后的 num 的值,此时会输出 20
std::cout << num << std::endl;
};
/* 注意事项:
* 1. [&num] 表示以引用的方式捕获变量 num,
* 2. 这意味着 lambda 表达式内部对 num 的操作会直接作用于外部定义的这个 num 变量
* 3. 因为它们共享同一块内存空间
*/
//3.调用定义好的lambda表达式,执行其内部的代码逻辑
lambda();
//4.再次输出 num 的值
std::cout << num << std::endl; //因为 lambda 表达式中通过引用捕获修改了 num,所以这里输出 20
return 0;
}

在这里插入图片描述
隐式捕获:可以让编译器自动推导捕获列表。 分为:
- 按值隐式捕获:
[=] - 按引用隐式捕获:
[&]
代码语言:javascript
AI代码解释
#include <iostream>
int main()
{
//1.定义两个局部变量
int num1 = 10;
int num2 = 20;
//2.lambda1:按值隐式捕获所有用到的外部变量
auto lambda1 = [=]()
{
//2.1:输出捕获的副本值
std::cout << num1 << " " << num2 << std::endl; // 输出: 10 20
};
/* 注意事项:
* [=]:表示以值捕获方式捕获所有外部变量
* lambda内部使用的是外部变量的副本,无法修改原始变量
*/
//3.lambda2:按引用隐式捕获所有用到的外部变量
auto lambda2 = [&]()
{
//3.1:直接修改外部的num1变量
num1 = 30;
//3.2:输出修改后的外部变量值
std::cout << num1 << " " << num2 << std::endl; // 输出: 30 20
};
/* 注意事项:
* [&]:表示以引用捕获方式捕获所有外部变量
* lambda内部使用的是外部变量的引用,可以直接修改原始变量
*/
//4.调用lambda1:输出捕获时的值副本
lambda1(); // 输出: 10 20
//5.调用lambda2:修改外部变量并输出
lambda2(); // 输出: 30 20
//6.验证外部变量确实被修改
std::cout << "外部num1: " << num1 << std::endl; // 输出: 30
return 0;
}

在这里插入图片描述
混合捕获:可以同时使用值捕获和引用捕获。
- 若开头用
[=](隐式传值),后续显式变量必须传引用- 如:
[=, &x](其他变量隐式传值,x显式传引用)
- 如:
- 若开头用
[&](隐式传引用),后续显式变量必须传值- 如:
[&, x](其他变量隐式传引用,x显式传值)
- 如:
所以:[=, &a] 表示按值捕获除 a 以外的所有用到的外部变量,而 a 按引用捕获。
总结:捕获列表的常见形式
|
捕获列表写法 |
含义 |
示例(外部变量 int a=10; double b=3.14;) |
|---|---|---|
|
[] |
不捕获任何外部变量 |
lambda 内无法使用 a、b |
|
[a] |
传值捕获 a(修改 lambda 内的 a 不影响外部) |
lambda 内 a 是 10 的副本 |
|
[&b] |
传引用捕获 b(修改 lambda 内的 b 会影响外部) |
lambda 内 b 是外部 b 的引用 |
|
[=] |
传值捕获所有用到的外部变量 |
lambda 内 a 是副本,b 是副本(修改不影响外部) |
|
[&] |
传引用捕获所有用到的外部变量 |
lambda 内 a、b 都是引用(修改会影响外部) |
|
[a, &b] |
混合捕获(a 传值,b 传引用) |
lambda 内 a 是副本,b 是引用 |
更多推荐



所有评论(0)