c14 lambda表达式
在 C++ 中,lambda 表达式(匿名函数)是一种便捷的函数定义方式,允许在需要函数的地方直接编写代码块,无需单独定义函数或函数对象。它是 C++11 引入的重要特性,广泛用于简化代码(尤其是配合 STL 算法)。
一、lambda 表达式的基本语法
lambda 表达式的基本格式如下:
[capture-list](parameter-list) mutable noexcept -> return-type {// 函数体
}
各部分含义:
capture-list
(捕获列表):指定外部变量如何被 lambda 内部访问(如按值捕获、按引用捕获)。parameter-list
(参数列表):与普通函数的参数列表相同(可省略,若为空可连同()
一起省略)。mutable
:可选,允许在 lambda 内部修改按值捕获的变量(默认不可修改)。noexcept
:可选,指定 lambda 不会抛出异常。-> return-type
(返回类型):可选,若函数体只有return
语句或无返回值,可省略(编译器自动推导)。- 函数体:lambda 要执行的代码。
二、最简单的 lambda 示例
最简化的 lambda 可以没有参数、捕获列表和返回类型:
#include <iostream>int main() {// 无参数、无捕获的lambdaauto hello = [] {std::cout << "Hello, Lambda!" << std::endl;};hello(); // 调用lambda,输出:Hello, Lambda!return 0;
}
三、核心组成部分详解
1. 捕获列表(capture-list
):访问外部变量
lambda 可以通过捕获列表访问定义它的作用域中的变量,主要有以下几种捕获方式:
捕获方式 | 含义 | 示例 |
---|---|---|
[] | 不捕获任何外部变量 | - |
[var] | 按值捕获var (复制一份,内部不可修改,除非用mutable ) | [x] { return x * 2; } |
[&var] | 按引用捕获var (可修改原变量) | [&x] { x++; } |
[=] | 按值捕获所有用到的外部变量 | [=] { return x + y; } |
[&] | 按引用捕获所有用到的外部变量 | [&] { x++; y++; } |
[=, &var] | 除var 按引用捕获外,其余按值捕获 | [=, &x] { x++; return y; } |
[&, var] | 除var 按值捕获外,其余按引用捕获 | [&, x] { y++; return x; } |
示例:
#include <iostream>int main() {int a = 10, b = 20;// 按值捕获a,按引用捕获bauto func = [a, &b] {// a = 30; // 错误:按值捕获的变量默认不可修改b = 30; // 正确:按引用捕获的变量可修改return a + b;};std::cout << func() << std::endl; // 输出:10 + 30 = 40std::cout << "b = " << b << std::endl; // 输出:30(原变量被修改)return 0;
}
若要修改按值捕获的变量,需加mutable
:
auto func = [a]() mutable {a = 30; // 允许修改(仅修改副本,不影响原变量)return a;
};
2. 参数列表与返回类型
lambda 的参数列表与普通函数一致,支持类型推导(C++14 起)、默认参数等:
// 带参数和返回类型的lambda
auto add = [](int x, int y) -> int {return x + y;
};
std::cout << add(2, 3) << std::endl; // 输出5// C++14起支持参数类型自动推导(auto)
auto multiply = [](auto x, auto y) {return x * y;
};
std::cout << multiply(2.5, 4) << std::endl; // 输出10.0
四、lambda 的典型应用场景
1. 作为 STL 算法的回调函数
lambda 最常用的场景是配合 STL 算法(如sort
、for_each
),简化代码:
#include <vector>
#include <algorithm>
#include <iostream>int main() {std::vector<int> nums = {3, 1, 4, 1, 5};// 用lambda作为排序的比较函数(降序)std::sort(nums.begin(), nums.end(), [](int a, int b) {return a > b; // 从大到小排序});// 用lambda遍历容器std::for_each(nums.begin(), nums.end(), [](int num) {std::cout << num << " "; // 输出:5 4 3 1 1});return 0;
}
2. 简化函数对象(仿函数)
在需要临时函数对象的场景(如线程、异步操作),lambda 比自定义仿函数更简洁:
#include <thread>
#include <iostream>int main() {int count = 0;// 用lambda创建线程(捕获count的引用)std::thread t([&count]() {for (int i = 0; i < 1000000; ++i) {count++;}});t.join();std::cout << "count = " << count << std::endl; // 输出:1000000return 0;
}
3. 捕获上下文的临时逻辑
lambda 可以捕获当前作用域的变量,实现依赖上下文的临时逻辑:
#include <iostream>
#include <functional>// 返回一个依赖于参数x的lambda
std::function<int(int)> makeAdder(int x) {return [x](int y) { // 捕获xreturn x + y;};
}int main() {auto add5 = makeAdder(5);auto add10 = makeAdder(10);std::cout << add5(3) << std::endl; // 输出:8std::cout << add10(3) << std::endl; // 输出:13return 0;
}
五、lambda 的本质
lambda 表达式在编译时会被转换为匿名的函数对象( functor,一种重载了operator()
的类),捕获的变量会成为该类的成员变量。因此,lambda 的大小取决于捕获变量的大小(无捕获的 lambda 大小为 1,类似空类)。
例如,以下 lambda:
int x = 10;
auto func = [x](int y) { return x + y; };
编译器会生成类似这样的类:
class LambdaFunc {
private:int x; // 捕获的变量
public:LambdaFunc(int x_) : x(x_) {} // 构造函数初始化捕获的变量int operator()(int y) const { // 重载()运算符return x + y;}
};
auto func = LambdaFunc(x);
六、注意事项
- 捕获列表的生命周期:按引用捕获的变量必须在 lambda 调用时仍有效,否则会导致悬空引用(如捕获局部变量后,在变量销毁后调用 lambda)。
mutable
的影响:仅允许修改按值捕获的副本,不影响原变量。- 与
std::function
结合:lambda 可赋值给std::function
(需包含<functional>
),用于存储或传递匿名函数。
总结
lambda 表达式是 C++ 中简化代码的强大工具,核心优势在于:
- 可以在需要函数的地方直接定义,避免编写独立函数或仿函数。
- 灵活的捕获机制,可访问外部变量。
- 与 STL 算法、线程等特性无缝配合,大幅提升代码可读性和开发效率。
掌握 lambda 是编写现代 C++ 代码的必备技能,尤其在泛型编程和异步编程中应用广泛。