一、为什么需要 lambda 匿名函数?
lambda(λ)是 C++11 引入的匿名函数,核心解决以下问题:
- 临时小函数无需单独定义:对于只使用一次的简单函数(如算法回调、临时逻辑),无需定义独立的函数 / 函数对象,代码更紧凑;
- 直接访问外部变量:lambda 可以灵活捕获当前作用域的变量,比普通函数更方便;
- 提升代码可读性:逻辑和调用位置就近编写,无需跳转到其他地方看函数定义。
简单来说:lambda 是 “即用即丢” 的临时函数,尤其适合作为 STL 算法(如sort、for_each)的参数。
二、lambda 的基本语法
lambda 的完整语法结构如下(部分可省略):
[capture] (parameters) mutable noexcept -> return_type { // 函数体(可执行逻辑) }| 组成部分 | 作用 | 是否可省略 |
|---|---|---|
[capture] | 捕获子句:指定 lambda 可以访问的外部变量(核心) | ❌ 必须有 |
(parameters) | 参数列表:和普通函数的参数列表一致 | ✅ 可省略 |
mutable | 允许修改值捕获的变量(默认值捕获不可修改) | ✅ 可省略 |
noexcept | 声明 lambda 不抛出异常 | ✅ 可省略 |
-> return_type | 返回值类型:可省略(C++11 起编译器自动推导,C++14 更完善) | ✅ 可省略 |
{ body } | 函数体:执行的逻辑 | ❌ 必须有 |
最简化示例(无参数、无捕获、无返回值)
#include <iostream> using namespace std; int main() { // 最简单的lambda:无捕获、无参数、无返回值 auto print_hello = []() { cout << "Hello, lambda!" << endl; }; // 调用lambda(和调用普通函数一样) print_hello(); // 输出:Hello, lambda! return 0; }包含所有常见 lambda 函数定义形式的汇总代码:
#include <iostream> #include <vector> #include <algorithm> #include <functional> #include <memory> using namespace std; // 测试用的类(用于类内lambda示例) class MyClass { private: int m_num = 100; public: void test_class_lambda(); }; // 用于返回lambda的函数(示例14) function<int(int)> return_lambda(); int main() { // =========== 1. 最基础的lambda:无捕获、无参数、无返回值 ============ // 特点:最简单的lambda,仅执行固定逻辑,无外部依赖 auto basic_lambda = []() { cout << "[基础lambda] 无捕获、无参数、无返回值" << endl; }; basic_lambda(); // =========== 2. 带参数的lambda(显式参数类型) ===================== // 特点:和普通函数一样接收参数,无返回值 auto param_lambda = [](int a, int b) { cout << "[带参数lambda] a + b = " << a + b << endl; }; param_lambda(10, 20); // =========== 3. 显式指定返回值的lambda ===================== // 特点:C++11多分支返回值推导失败时必须用,也可提升可读性 auto return_type_lambda = [](int a, int b) -> double { if (b == 0) return 0.0; return static_cast<double>(a) / b; // 强制转double }; cout << "[显式返回值lambda] 5/2 = " << return_type_lambda(5, 2) << endl; // =========== 4. 值捕获外部变量的lambda ===================== // 特点:拷贝外部变量到lambda内部,默认不可修改 int val1 = 10; auto value_capture_lambda = [val1]() { // val1 = 20; // 错误:值捕获默认不可修改 cout << "[值捕获lambda] 捕获的val1 = " << val1 << endl; }; value_capture_lambda(); // =========== 5. mutable修饰的值捕获lambda ===================== // 特点:允许修改值捕获的变量(仅修改内部拷贝,不影响外部) auto mutable_capture_lambda = [val1]() mutable { val1 = 20; // mutable允许修改内部拷贝 cout << "[mutable值捕获lambda] 内部修改后的val1 = " << val1 << endl; }; mutable_capture_lambda(); cout << "[mutable值捕获lambda] 外部val1仍为 = " << val1 << endl; // 仍为10 // =========== 6. 引用捕获外部变量的lambda ===================== // 特点:引用外部变量,修改会影响外部 int val2 = 30; auto ref_capture_lambda = [&val2]() { val2 = 40; // 引用捕获可直接修改外部变量 cout << "[引用捕获lambda] 修改后的val2 = " << val2 << endl; }; ref_capture_lambda(); cout << "[引用捕获lambda] 外部val2同步修改为 = " << val2 << endl; // 变为40 // =========== 7. 隐式捕获lambda ===================== // 7.1 隐式值捕获(=):捕获所有外部变量(值拷贝) int a = 5, b = 6; auto implicit_val_capture = [=]() { cout << "[隐式值捕获] a = " << a << ", b = " << b << endl; }; implicit_val_capture(); // 7.2 隐式引用捕获(&):捕获所有外部变量(引用) auto implicit_ref_capture = [&]() { a = 50; b = 60; cout << "[隐式引用捕获] 修改后 a = " << a << ", b = " << b << endl; }; implicit_ref_capture(); // =========== 8. 混合捕获lambda ===================== // 特点:默认捕获 + 指定捕获(优先级更高) int x = 100, y = 200; auto mix_capture_lambda = [=, &x]() { // 默认值捕获,仅x引用捕获 x = 1000; // 引用捕获可修改 // y = 2000; // 错误:y是值捕获,不可修改(需加mutable) cout << "[混合捕获lambda] x = " << x << ", y = " << y << endl; }; mix_capture_lambda(); // =========== 9. 泛型lambda(C++14+) ===================== // 特点:参数用auto,支持任意类型参数(类似模板函数) auto generic_lambda = [](auto val1, auto val2) { cout << "[泛型lambda] val1 = " << val1 << ", val2 = " << val2 << endl; }; generic_lambda(123, 3.14); // int + double generic_lambda("hello", string("cpp")); // const char* + string // =========== 10. 立即调用的匿名lambda ===================== // 特点:定义后立即执行,无需赋值给变量,一次性使用 cout << "[立即调用lambda] 执行结果:"; []() { cout << "匿名lambda立即执行" << endl; }(); // 最后的()表示立即调用 // =========== 11. 移动捕获lambda(C++14+) ===================== // 特点:捕获仅可移动的对象(如unique_ptr),转移所有权 unique_ptr<int> ptr = make_unique<int>(500); auto move_capture_lambda = [p = move(ptr)]() { cout << "[移动捕获lambda] 捕获的unique_ptr值 = " << *p << endl; }; move_capture_lambda(); // ptr已为空(所有权转移) if (!ptr) cout << "[移动捕获lambda] 外部ptr已空" << endl; // =========== 12. 作为STL算法参数的lambda ===================== // 特点:就地定义回调逻辑,无需单独定义普通函数 vector<int> vec = {5,2,9,1,5,6}; sort(vec.begin(), vec.end(), [](int a, int b) { return a > b; // 降序排序规则 }); cout << "[STL算法lambda] 降序排序结果:"; for (int n : vec) cout << n << " "; cout << endl; // =========== 13. 类内捕获this的lambda ===================== // 特点:访问类的成员变量/函数,无需传this指针 MyClass obj; obj.test_class_lambda(); // =========== 14. 作为函数返回值的lambda ===================== // 特点:需用std::function包装,可返回带捕获的lambda auto returned_lambda = return_lambda(); cout << "[返回lambda] 调用结果:" << returned_lambda(5) << endl; return 0; } // 类内lambda实现(示例13) void MyClass::test_class_lambda() { auto class_lambda = [this]() { m_num = 200; // 捕获this,访问类成员变量 cout << "[类内lambda] 修改后的m_num = " << m_num << endl; }; class_lambda(); } // 返回lambda的函数实现(示例14) function<int(int)> return_lambda() { int base = 10; // 返回带值捕获的lambda,用std::function包装 return [base](int x) { return x + base; }; }三、核心:捕获子句([capture])
捕获子句是 lambda 最关键的部分,决定了 lambda 能访问哪些外部变量,以及以何种方式访问(值 / 引用)。
1. 捕获方式分类
| 捕获语法 | 含义 |
|---|---|
[] | 空捕获:不捕获任何外部变量 |
[var] | 值捕获:拷贝变量var到 lambda 内部(lambda 内修改不影响外部) |
[&var] | 引用捕获:引用变量var(lambda 内修改会影响外部) |
[=] | 隐式值捕获:捕获所有外部变量(值拷贝) |
[&] | 隐式引用捕获:捕获所有外部变量(引用) |
[this] | 类内 lambda 捕获当前对象的this指针(可访问类的成员变量 / 函数) |
[=, &var] | 混合捕获:默认值捕获所有变量,仅var用引用捕获(C++11 起支持) |
[&, var] | 混合捕获:默认引用捕获所有变量,仅var用值捕获 |
2. 捕获示例详解
(1)值捕获 vs 引用捕获
#include <iostream> using namespace std; int main() { int a = 10, b = 20; // 1. 值捕获:拷贝a、b到lambda内部 auto lambda_val = [a, b]() { // a = 100; // 错误!值捕获的变量默认不可修改(需加mutable) cout << "值捕获:a=" << a << ", b=" << b << endl; // 输出:10,20 }; lambda_val(); // 2. 引用捕获:引用a、b(修改会影响外部) auto lambda_ref = [&a, &b]() { a = 100; b = 200; cout << "引用捕获修改后:a=" << a << ", b=" << b << endl; // 输出:100,200 }; lambda_ref(); cout << "外部变量:a=" << a << ", b=" << b << endl; // 输出:100,200(外部被修改) // 3. mutable:允许修改值捕获的变量(仅修改lambda内部拷贝,不影响外部) auto lambda_mutable = [a]() mutable { a = 999; cout << "mutable值捕获:a=" << a << endl; // 输出:999 }; lambda_mutable(); cout << "外部a:" << a << endl; // 输出:100(外部未变) return 0; }(2)隐式捕获
#include <iostream> using namespace std; int main() { int x = 5, y = 6; // 隐式值捕获:捕获所有外部变量(x、y),值拷贝 auto lambda_implicit_val = [=]() { cout << "隐式值捕获:x=" << x << ", y=" << y << endl; // 输出:5,6 }; lambda_implicit_val(); // 隐式引用捕获:捕获所有外部变量(x、y),引用 auto lambda_implicit_ref = [&]() { x = 50; y = 60; cout << "隐式引用捕获:x=" << x << ", y=" << y << endl; // 输出:50,60 }; lambda_implicit_ref(); // 混合捕获:默认值捕获,仅x用引用 auto lambda_mix = [=, &x]() { x = 500; // 引用捕获,可修改且影响外部 // y = 600; // 错误!y是值捕获,不可修改(需加mutable) cout << "混合捕获:x=" << x << ", y=" << y << endl; // 输出:500,60 }; lambda_mix(); return 0; }(3)类内捕获 this
lambda 在类的成员函数中可以捕获this指针,从而访问类的成员变量 / 函数:
#include <iostream> using namespace std; class MyClass { private: int num = 100; public: void func() { // 捕获this,访问类成员 auto lambda = [this]() { num = 200; // 修改成员变量 cout << "类内lambda:num=" << num << endl; // 输出:200 }; lambda(); } int get_num() { return num; } }; int main() { MyClass obj; obj.func(); cout << "外部访问:" << obj.get_num() << endl; // 输出:200 return 0; }四、lambda 的进阶特性
1. 自动推导返回值(省略 -> return_type)
C++11 起,若 lambda 函数体只有return语句,编译器可自动推导返回值;C++14 后支持任意函数体的返回值推导:
#include <iostream> using namespace std; int main() { // C++11支持:单return语句自动推导 auto add = [](int a, int b) { return a + b; // 自动推导返回值为int }; cout << add(3,5) << endl; // 输出:8 // C++14支持:多分支自动推导(需返回值类型一致) auto calc = [](int a, int b, char op) { if (op == '+') return a + b; else return a - b; }; cout << calc(10, 3, '-') << endl; // 输出:7 return 0; }2. C++11 多分支返回值推导失败(必须显式指定)
C++11 对 lambda 返回值自动推导的限制:仅当函数体只有单个return语句时能推导,多分支会报错,此时必须用->显式声明返回值类型。
#include <iostream> using namespace std; int main() { // 错误示例(C++11):多分支return,自动推导失败 // auto calc = [](int a, int b, char op) { // if (op == '+') return a + b; // 返回int // else return 0.0; // 返回double,C++11无法推导 // }; // 正确示例:显式指定返回值类型为double auto calc = [](int a, int b, char op) -> double { if (op == '+') return a + b; // int自动转为double else return 0.0; // 原生double }; cout << calc(10, 20, '+') << endl; // 输出30.0(double类型) cout << calc(10, 20, '-') << endl; // 输出0.0(double类型) return 0; }3. 泛型 lambda(C++14)
参数列表中使用auto,让 lambda 支持任意类型的参数(类似模板函数):
#include <iostream> #include <string> using namespace std; int main() { // 泛型lambda:支持任意类型的参数 auto print = [](auto val) { cout << "泛型lambda:" << val << endl; }; print(123); // 输出:123(int) print(3.14); // 输出:3.14(double) print("hello"); // 输出:hello(const char*) print(string("cpp")); // 输出:cpp(string) return 0; }4. lambda 作为函数参数 / 返回值
lambda 可作为 STL 算法的参数(最常用场景),也可作为函数返回值(需用std::function):
#include <iostream> #include <vector> #include <algorithm> #include <functional> // std::function头文件 using namespace std; // lambda作为函数参数(用std::function接收) void process_data(int a, int b, function<int(int, int)> func) { cout << "处理结果:" << func(a, b) << endl; } // lambda作为返回值 function<int(int)> get_lambda() { int base = 10; // 返回一个lambda(捕获base) return [base](int x) { return x + base; }; } int main() { // 1. lambda作为STL算法参数(排序) vector<int> vec = {5,2,9,1,5,6}; // 自定义排序规则:降序 sort(vec.begin(), vec.end(), [](int a, int b) { return a > b; }); cout << "降序排序:"; for (int n : vec) cout << n << " "; // 输出:9 6 5 5 2 1 cout << endl; // 2. lambda作为函数参数 process_data(10, 20, [](int a, int b) { return a * b; }); // 输出:200 // 3. lambda作为返回值 auto func = get_lambda(); cout << "返回lambda调用:" << func(5) << endl; // 输出:15(5+10) return 0; }5. 捕获表达式(C++14):移动语义捕获
可以用std::move捕获仅可移动的变量(如std::unique_ptr):
#include <iostream> #include <memory> using namespace std; int main() { unique_ptr<int> ptr = make_unique<int>(100); // 移动捕获:将ptr的所有权转移到lambda内部 auto lambda = [p = move(ptr)]() { cout << "移动捕获:" << *p << endl; // 输出:100 }; lambda(); // ptr已为空(所有权转移) if (!ptr) cout << "外部ptr已空" << endl; return 0; }五、lambda 的本质(底层逻辑)
lambda 并非 “魔法”,编译器会将 lambda 转换为匿名的函数对象(仿函数):
- 捕获子句对应函数对象的成员变量(值捕获 = 拷贝构造,引用捕获 = 引用成员);
- lambda 的参数 / 函数体对应函数对象的
operator()重载; mutable对应operator()重载为非 const(值捕获的成员变量默认是 const,需 mutable 解除)。
比如以下 lambda:
int a = 10; auto lambda = [a]() mutable { a++; };编译器会生成类似这样的代码:
// 编译器生成的匿名类(伪代码) class __lambda_12345 { // 12345是编译器生成的唯一标识 private: int a; // 值捕获的变量 public: __lambda_12345(int a_) : a(a_) {} void operator()() { // mutable对应非const a++; } }; // 实际调用 __lambda_12345 lambda(10); lambda();六、lambda 的使用注意事项
- 引用捕获的生命周期:若 lambda 的生命周期超过被引用变量的生命周期,会导致悬空引用(访问已释放的内存);
// 错误示例:返回引用捕获局部变量的lambda auto bad_lambda() { int x = 10; return [&x]() { cout << x << endl; }; // x已销毁,调用时未定义行为 } - 值捕获是拷贝:值捕获的变量是 lambda 创建时的拷贝,而非实时值;
this捕获的风险:若 lambda 的生命周期超过对象生命周期,this会悬空;- lambda 不能被重载:lambda 是匿名的,无法像普通函数一样重载。
七、lambda 对比 普通函数
| 对比维度 | 普通函数 | lambda 匿名函数 |
|---|---|---|
| 定义位置 | 只能定义在全局 / 命名空间 / 类内(不能在函数内定义普通函数) | 可定义在任意代码块内(函数内、循环内、if 内) |
| 访问外部变量 | 只能通过「传参」或「全局变量」(无其他方式) | 可通过「捕获子句」灵活访问当前作用域的变量(值 / 引用) |
| 复用性 | 天生支持复用(定义一次,多处调用) | 默认匿名,仅当前作用域可用(如需复用需赋值给变量) |
| 命名要求 | 必须有唯一名称(易造成命名污染) | 无需命名(一次性使用),也可赋值给变量命名 |
| 类型特性 | 固定的函数类型,无法直接作为参数传递(需函数指针 / 包装) | 编译器生成匿名函数对象,可直接作为参数(如 STL 算法回调) |
| 类内场景 | 需传this指针才能访问类成员 | 可直接捕获this,便捷访问类成员变量 / 函数 |
| 回调便捷性 | 作为回调需借助函数指针 /std::function包装,步骤繁琐;若回调需访问外部状态,需额外封装(全局变量 / 类对象),代码冗余 | 可直接作为回调参数传递(无需包装);若需访问外部状态,通过捕获子句即可实现,无需额外封装,代码简洁 |
场景 1:访问外部变量(lambda 最核心的优势)
普通函数要访问当前作用域的变量,只有两种方式:传参(麻烦)或全局变量(危险,易污染);而 lambda 可以通过「捕获子句」直接访问,无需额外操作。
普通函数实现(麻烦 / 不优雅)
#include <iostream> using namespace std; // 普通函数:要访问外部的factor,必须传参 int multiply(int num, int factor) { return num * factor; } int main() { int factor = 5; // 局部变量 // 调用普通函数:必须手动传factor cout << multiply(10, factor) << endl; // 输出50 cout << multiply(20, factor) << endl; // 输出100 return 0; }lambda 实现(简洁)
#include <iostream> using namespace std; int main() { int factor = 5; // 局部变量 // lambda:直接捕获factor,无需传参 auto multiply = [factor](int num) { return num * factor; }; // 调用时只需传num,factor已被捕获 cout << multiply(10) << endl; // 输出50 cout << multiply(20) << endl; // 输出100 return 0; }场景 2:STL 算法回调(就地定义,代码不割裂)
普通函数作为 STL 算法的回调时,需要单独定义(代码跳来跳去);lambda 可以「就地定义」,逻辑和调用位置在一起,可读性大幅提升。
普通函数实现(代码割裂)
#include <iostream> #include <vector> #include <algorithm> using namespace std; // 普通函数:排序规则(降序),需单独定义 bool compare(int a, int b) { return a > b; } int main() { vector<int> vec = {5,2,9,1,5,6}; // 调用sort,传入普通函数名 sort(vec.begin(), vec.end(), compare); for (int n : vec) cout << n << " "; // 9 6 5 5 2 1 return 0; }lambda 实现(就地定义,代码连贯)
#include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { vector<int> vec = {5,2,9,1,5,6}; // lambda:排序规则直接写在sort参数里,无需跳转到其他位置 sort(vec.begin(), vec.end(), [](int a, int b) { return a > b; // 降序规则 }); for (int n : vec) cout << n << " "; // 9 6 5 5 2 1 return 0; }场景 3:一次性小逻辑(避免命名污染)
对于只使用一次的小逻辑,普通函数需要起名字(哪怕只用一次),容易造成 “命名污染”;lambda 匿名使用,用完即丢,无需命名。
普通函数实现(命名污染)
#include <iostream> using namespace std; // 只用一次的小函数,却要起名字(比如叫print_welcome) void print_welcome() { cout << "欢迎使用本程序!" << endl; } int main() { // 仅调用一次 print_welcome(); // 后续再也不用这个函数,但它的名字仍占用命名空间 return 0; }lambda 实现(匿名,无命名污染)
#include <iostream> using namespace std; int main() { // 匿名lambda:直接定义+调用,无需命名,用完即销毁 []() { cout << "欢迎使用本程序!" << endl; }(); // 最后的()表示立即调用 return 0; }场景 4:类内访问成员(lambda 捕获 this 更便捷)
普通函数要访问类的成员变量,需要传this指针或对象;lambda 可以直接捕获this,一键访问类成员。
普通函数实现(麻烦)
#include <iostream> using namespace std; class MyClass { private: int base = 100; public: // 普通函数:需传this指针才能访问base static int add(MyClass* obj, int num) { return obj->base + num; } void func() { // 调用普通函数,必须传this cout << add(this, 50) << endl; // 输出150 } }; int main() { MyClass obj; obj.func(); return 0; }lambda 实现(简洁)
#include <iostream> using namespace std; class MyClass { private: int base = 100; public: void func() { // lambda:捕获this,直接访问base auto add = [this](int num) { return this->base + num; // 也可省略this->,直接base }; cout << add(50) << endl; // 输出150 } }; int main() { MyClass obj; obj.func(); return 0; }