C++ 匿名函数 lambda

发布时间:2026/8/5 14:55:10
C++ 匿名函数 lambda 一、为什么需要 lambda 匿名函数lambdaλ是 C11 引入的匿名函数核心解决以下问题临时小函数无需单独定义对于只使用一次的简单函数如算法回调、临时逻辑无需定义独立的函数 / 函数对象代码更紧凑直接访问外部变量lambda 可以灵活捕获当前作用域的变量比普通函数更方便提升代码可读性逻辑和调用位置就近编写无需跳转到其他地方看函数定义。简单来说lambda 是 “即用即丢” 的临时函数尤其适合作为 STL 算法如sort、for_each的参数。二、lambda 的基本语法lambda 的完整语法结构如下部分可省略[capture] (parameters) mutable noexcept - return_type { // 函数体可执行逻辑 }组成部分作用是否可省略[capture]捕获子句指定 lambda 可以访问的外部变量核心❌ 必须有(parameters)参数列表和普通函数的参数列表一致✅ 可省略mutable允许修改值捕获的变量默认值捕获不可修改✅ 可省略noexcept声明 lambda 不抛出异常✅ 可省略- return_type返回值类型可省略C11 起编译器自动推导C14 更完善✅ 可省略{ 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 functionint(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 // 特点C11多分支返回值推导失败时必须用也可提升可读性 auto return_type_lambda [](int a, int b) - double { if (b 0) return 0.0; return static_castdouble(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. 泛型lambdaC14 // 特点参数用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. 移动捕获lambdaC14 // 特点捕获仅可移动的对象如unique_ptr转移所有权 unique_ptrint ptr make_uniqueint(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 // 特点就地定义回调逻辑无需单独定义普通函数 vectorint 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 functionint(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]引用捕获引用变量varlambda 内修改会影响外部[]隐式值捕获捕获所有外部变量值拷贝[]隐式引用捕获捕获所有外部变量引用[this]类内 lambda 捕获当前对象的this指针可访问类的成员变量 / 函数[, var]混合捕获默认值捕获所有变量仅var用引用捕获C11 起支持[, 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类内捕获 thislambda 在类的成员函数中可以捕获this指针从而访问类的成员变量 / 函数#include iostream using namespace std; class MyClass { private: int num 100; public: void func() { // 捕获this访问类成员 auto lambda [this]() { num 200; // 修改成员变量 cout 类内lambdanum 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_typeC11 起若 lambda 函数体只有return语句编译器可自动推导返回值C14 后支持任意函数体的返回值推导#include iostream using namespace std; int main() { // C11支持单return语句自动推导 auto add [](int a, int b) { return a b; // 自动推导返回值为int }; cout add(3,5) endl; // 输出8 // C14支持多分支自动推导需返回值类型一致 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. C11 多分支返回值推导失败必须显式指定C11 对 lambda 返回值自动推导的限制仅当函数体只有单个return语句时能推导多分支会报错此时必须用-显式声明返回值类型。#include iostream using namespace std; int main() { // 错误示例C11多分支return自动推导失败 // auto calc [](int a, int b, char op) { // if (op ) return a b; // 返回int // else return 0.0; // 返回doubleC11无法推导 // }; // 正确示例显式指定返回值类型为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.0double类型 cout calc(10, 20, -) endl; // 输出0.0double类型 return 0; }3. 泛型 lambdaC14参数列表中使用auto让 lambda 支持任意类型的参数类似模板函数#include iostream #include string using namespace std; int main() { // 泛型lambda支持任意类型的参数 auto print [](auto val) { cout 泛型lambda val endl; }; print(123); // 输出123int print(3.14); // 输出3.14double print(hello); // 输出helloconst char* print(string(cpp)); // 输出cppstring 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, functionint(int, int) func) { cout 处理结果 func(a, b) endl; } // lambda作为返回值 functionint(int) get_lambda() { int base 10; // 返回一个lambda捕获base return [base](int x) { return x base; }; } int main() { // 1. lambda作为STL算法参数排序 vectorint 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; // 输出15510 return 0; }5. 捕获表达式C14移动语义捕获可以用std::move捕获仅可移动的变量如std::unique_ptr#include iostream #include memory using namespace std; int main() { unique_ptrint ptr make_uniqueint(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 解除。比如以下 lambdaint 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; }; // 调用时只需传numfactor已被捕获 cout multiply(10) endl; // 输出50 cout multiply(20) endl; // 输出100 return 0; }场景 2STL 算法回调就地定义代码不割裂普通函数作为 STL 算法的回调时需要单独定义代码跳来跳去lambda 可以「就地定义」逻辑和调用位置在一起可读性大幅提升。普通函数实现代码割裂#include iostream #include vector #include algorithm using namespace std; // 普通函数排序规则降序需单独定义 bool compare(int a, int b) { return a b; } int main() { vectorint 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() { vectorint 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; }