一、引入
在 C++ 中,我们通常使用函数来完成特定的功能。但有时候,我们需要在一个函数内部定义一个小型的功能块,这时如果单独写一个函数会显得繁琐。C++11 引入了 Lambda 函数,它是一种匿名函数,可以在需要的地方直接定义和使用,非常适合用于简化代码。
二、Lambda函数的基本用法
1.语法模板(看不懂没关系,看完实例就理解了)
[捕获列表](参数列表) -> 返回类型 {函数体
}
逐部分讲解:
1.捕获列表 []:
用于捕获外部变量,决定 Lambda 函数如何访问外部作用域的变量。
常见形式:
[]:不捕获任何变量。
[=]:以值的方式捕获所有外部变量。
[&]:以引用的方式捕获所有外部变量。
[x, &y]:以值捕获 x,以引用捕获 y。
2.参数列表 ():
和普通函数的参数列表一样,用于传递参数。
3.返回类型 -> 返回类型:
可以省略,编译器会自动推导返回类型。
如果函数体中有多个返回语句,且类型不一致,则需要显式指定返回类型。
4.函数体 {}:
实现具体的功能逻辑。
示例:
auto add = [](int a, int b) -> int {return a + b;
};
std::cout << add(2, 3); // 输出: 5
三、 Lambda 函数的捕获列表详解
示例 1:不捕获任何变量
int x = 10;
auto func = []() {// x 不可访问,编译错误// std::cout << x;
};
示例 2:以值的方式捕获变量
int x = 10;
auto func = [x]() {std::cout << x; // 输出: 10
};
x = 20;
func(); // 输出仍然是 10,因为捕获的是值
此时函数捕获的是x的数值:10,而不是x这个变量,因此外界对x更改不会影响函数中10这个数
示例 3:以引用的方式捕获变量
int x = 10;
auto func = [&x]() {std::cout << x; // 输出: 10
};
x = 20;
func(); // 输出: 20,因为捕获的是引用
此时函数捕获的是x这个变量,区别于上面那种
示例 4:混合捕获
int x = 10, y = 20;
auto func = [x, &y]() {std::cout << x << " " << y; // 输出: 10 20y = 30; // 修改 y 的值
};
func();
std::cout << y; // 输出: 30
四、Lambda 函数的实际应用
示例 :STL 算法中的 Lambda 函数
#include <iostream>
#include <vector>
#include <algorithm>int main() {std::vector<int> nums = {3, 1, 4, 1, 5, 9};std::sort(nums.begin(), nums.end(), [](int a, int b) {return a > b; // 降序排序});for (int num : nums) {std::cout << num << " "; // 输出: 9 5 4 3 1 1}return 0;
}
五、总结与注意事项
总结:
-
Lambda 函数是一种匿名函数,适合用于简化代码和实现小型功能。
-
捕获列表决定了 Lambda 函数如何访问外部变量。
-
Lambda 函数常用于 STL 算法和回调函数中。
注意事项:
-
捕获列表的使用需要谨慎,避免不必要的值拷贝或引用捕获。
-
如果 Lambda 函数的逻辑较复杂,建议单独写一个普通函数。
六、易混辨析与深入理解
1.[ ]和 < >的大乱斗
(1)两者区别
特性 | [] (捕获列表) | <> (模板参数列表) |
---|---|---|
用途 | 用于 Lambda 函数,定义如何捕获外部变量 | 用于模板,定义类型参数 |
位置 | 出现在 Lambda 函数的开头 | 出现在模板类或模板函数的定义或调用处 |
内容 | 捕获方式(值、引用等) | 类型参数(如 int 等) |
它们的作用域和语法规则是独立的,没有任何关联。
(2)如何选取
int x = 10;
auto func = [x](int y) -> int {return x + y; // 捕获 x,参数 y
};
std::cout << func(5); // 输出: 15
例如上面的程序,为什么不写成下面的:
int x = 10;
auto func = [](int x,int y) -> int {return x + y;
};
std::cout << func(x,5); // 输出: 15
分析解答:
特性 | 直接传递参数 | 使用捕获列表 |
---|---|---|
依赖外部变量 | 不依赖,x 通过参数传递 | 依赖,x 通过捕获列表捕获 |
参数传递 | 每次调用都需要显式传递 x | x 在 Lambda 函数内部直接可用 |
性能 | 可能产生额外的拷贝开销 | 引用捕获可以避免拷贝开销 |
代码可读性 | 更直观,逻辑清晰 | 可能降低可读性,尤其是捕获列表复杂时 |
适用场景 | 适合简单的、独立的功能 | 适合需要封装外部状态的场景 |
(3)为什么选择捕获列表?
场景 1:封装外部状态
int x = 10;
auto func = [x]() {std::cout << "Captured x: " << x;
};
x = 20; // 修改外部 x
func(); // 输出: Captured x: 10
场景 2:避免重复传递参数
int x = 10;
auto func = [x](int y) {return x + y; // 不需要每次调用都传递 x
};
std::cout << func(5); // 输出: 15
std::cout << func(10); // 输出: 20
场景 3:引用捕获避免拷贝
std::vector<int> data = {1, 2, 3};
auto func = [&data]() {data.push_back(4); // 修改外部 data
};
func();
std::cout << data.size(); // 输出: 4
2.[&]
和 [&x]的区别
[&]
:以引用的方式捕获所有外部变量
作用:捕获所有外部变量,并以引用的方式访问它们。
-
特点:
-
简洁,不需要显式列出所有变量。
-
适合需要捕获多个变量的场景。
-
可读性较差,因为不清楚具体捕获了哪些变量。
-
[&x]
:以引用的方式捕获特定变量
作用:只捕获指定的变量 x
,并以引用的方式访问它。
-
特点:
-
明确,清楚地知道捕获了哪个变量。
-
适合只需要捕获少量变量的场景。
-
可读性较好,但可能会显得冗长。
-