【C++】一篇文章学会使用C++ 11 Lambda表达式
Lambda表达式的定义与设计初衷什么是LambdaLambda是匿名函数unnamed function允许在代码中“就地定义、就地使用”无需像普通函数那样先声明/定义再调用也无需写函数对象仿函数的完整类结构。设计初衷简化代码尤其是短小的回调、算法自定义逻辑替代传统的函数指针/仿函数提升代码可读性和开发效率。适用场景STL算法sort/for_each等、异步回调线程/协程、局部短小逻辑封装。闭包ClosureLambda的底层本质Lambda表达式的底层实现是什么闭包的定义计算机科学层面闭包是“函数体 该函数绑定的外部变量环境”的组合能捕获并保留外部作用域的变量即使外部变量超出作用域闭包仍可访问。C中Lambda的底层编译器会将Lambda表达式编译为一个匿名的函数对象仿函数struct/class捕获列表中的变量会成为该类的成员变量Lambda的调用本质是调用该仿函数的operator()。// 示例Lambda的底层等价形式intx10;autolambda[x](inty){returnxy;};// 编译器等价生成如下代码简化版class__AnonymousLambda{private:intx;// 捕获的变量作为成员变量public:__AnonymousLambda(intx_):x(x_){}// 构造函数初始化捕获的变量intoperator()(inty)const{// 默认const对应Lambda不可修改按值捕获的变量returnxy;}};__AnonymousLambdalambda(x);// 等价于Lambda定义Lambda的完整语法格式[capture-list](parameter-list)mutablenoexcept-return-type{function-body}组成部分必选/可选核心说明[capture-list]必选捕获列表绑定外部作用域的变量到Lambda内部(parameter-list)可选参数列表和普通函数一致可空、可带默认参数C14支持auto参数mutable可选解除operator()的const限制允许修改按值捕获的变量仅影响副本noexcept可选声明Lambda不会抛出异常C11起支持- return-type可选返回值类型尾返回类型编译器可自动推导单return语句时多分支需显式指定{ function-body }必选函数体执行具体逻辑语法简化示例// 最简写法无捕获、无参数、无返回值autoprint[](){coutHello Lambdaendl;};print();// 自动推导返回值单returnautoadd[](inta,intb){returnab;};// 等价于-int// C14 auto参数泛型Lambdaautosum[](autoa,autob){returnab;};sum(1,2);// intsum(1.5,2.5);// double// mutable修改按值捕获的变量intx10;automodify[x]()mutable{x20;coutxendl;};// 输出20副本modify();coutxendl;// 输出10原变量不变捕获列表捕获列表是Lambda最核心的部分以下是完整的捕获规则基础捕获方式捕获语法类型特点风险/注意事项[x]按值捕获拷贝外部变量x到Lambda内部默认不可修改加mutable可改修改不影响外部拷贝开销大对象、捕获的是绑定时的副本[x]按引用捕获引用外部变量x修改会同步到外部无拷贝开销悬垂引用外部变量提前销毁引用失效[]全部按值捕获所有外部变量的副本简洁但可能捕获冗余变量同按值捕获的风险[]全部按引用捕获所有外部变量的引用极简但风险高悬垂引用、意外修改外部变量混合/特殊捕获捕获语法说明[, x]除x按引用捕获其余所有外部变量按值捕获优先级高于[, x]除x按值捕获其余所有外部变量按引用捕获优先级高于[this]类内Lambda捕获当前对象的this指针可访问类的成员变量/成员函数C11[*this]捕获当前对象的副本C17而非this指针避免悬垂this[]空捕获不捕获任何外部变量Lambda仅能访问全局变量/参数[x 10]初始化捕获C14定义Lambda内部的变量无需外部变量[ptr std::move(obj)]移动捕获C14将外部对象移动到Lambda内部针对不可拷贝对象捕获示例// 1. 类内Lambda捕获thisclassTest{private:intnum100;public:voidfunc(){autolambda[this](){coutnumendl;};// 访问成员变量lambda();// 输出100}};// 2. 移动捕获C14std::unique_ptrintptr(newint(10));autolambda[pstd::move(ptr)](){cout*pendl;};lambda();// 输出10// ptr已被移动此处不可再使用// 3. 悬垂引用autobadLambda(){intx10;return[x](){coutxendl;};// 捕获局部变量的引用}autofuncbadLambda();func();// 未定义行为x已销毁引用悬垂Lambda的特性Lambda的可调用性与存储Lambda是闭包类型的临时对象可赋值给auto变量或函数对象std::function无捕获的Lambda可隐式转换为函数指针有捕获的不行// 无捕获可转函数指针void(*fp)()[](){coutHelloendl;};fp();// 有捕获不可转函数指针编译报错intx10;// void (*fp2)() [x]() { cout x endl; }; // error// 需用std::function包装std::functionvoid()f[x](){coutxendl;};f();Lambda的生命周期与捕获变量按值捕获的变量在Lambda定义时完成拷贝后续外部变量修改不影响Lambda内部按引用捕获的变量生命周期依赖外部变量若外部变量销毁Lambda引用失效悬垂引用线程场景的坑线程中使用按引用捕获的局部变量主线程退出后变量销毁子线程访问崩溃// 错误示例voidthreadTest(){intx10;std::threadt([x](){std::this_thread::sleep_for(std::chrono::seconds(1));coutxendl;// x已销毁未定义行为});t.detach();// 主线程直接退出}// 正确示例按值捕获voidthreadTestFixed(){intx10;std::threadt([x](){std::this_thread::sleep_for(std::chrono::seconds(1));coutxendl;// 安全x是副本});t.join();}Lambda与STL的结合// 1. sort自定义排序std::vectorintv{3,1,4,1,5};std::sort(v.begin(),v.end(),[](inta,intb){returnab;// 降序排序});// 2. for_each遍历std::for_each(v.begin(),v.end(),[](intn){coutn ;// 输出5 4 3 1 1});// 3. find_if查找autoitstd::find_if(v.begin(),v.end(),[](intn){returnn4;// 查找值为4的元素});易错点mutable的作用范围仅允许修改按值捕获的副本不会影响外部变量按引用捕获的变量无需mutable即可修改但有风险悬垂引用最常见错误Lambda返回按引用捕获的局部变量、线程中引用主线程局部变量都会导致未定义行为捕获this的风险类对象销毁后Lambda若持有this指针访问成员变量会崩溃C17的[*this]可解决Lambda的const特性默认情况下Lambda的operator()是const成员函数因此按值捕获的变量不可修改mutable解除此限制C版本差异C11不支持auto参数、初始化捕获C14才支持C17支持[*this]捕获对象副本。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2409452.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!