C++STL教程:函数对象与Lambda表达式 本文是 C 系列教程的第 14 篇。上一篇讲解了迭代器与算法库本篇讲解函数对象与 Lambda 表达式仿函数、lambda 语法捕获列表/参数/返回类型、值捕获与引用捕获、mutable、std::function、std::bind、与算法配合。一、函数对象仿函数1.1 什么是函数对象函数对象Functor是重载了operator()的类对象可以像函数一样调用。与普通函数相比它可以保存状态。#includeiostreamusingnamespacestd;// 函数对象重载 operator()classAdder{private:intbase;// 保存状态public:Adder(intb):base(b){}intoperator()(intvalue)const{returnvaluebase;}};intmain(){Adderadd5(5);Adderadd10(10);coutadd5(3)endl;// 8像函数一样调用coutadd10(3)endl;// 13return0;}1.2 带状态的函数对象函数对象的优势是保存状态这是普通函数做不到的#includeiostreamusingnamespacestd;classCounter{private:intcount0;public:intoperator()(){returncount;// 每次调用状态递增}};intmain(){Counter counter;coutcounter()endl;// 1coutcounter()endl;// 2coutcounter()endl;// 3return0;}1.3 函数对象与算法的配合#includeiostream#includevector#includealgorithmusingnamespacestd;// 阈值过滤器classGreaterThan{private:intthreshold;public:GreaterThan(intt):threshold(t){}booloperator()(intvalue)const{returnvaluethreshold;}};intmain(){vectorintnums{3,8,1,6,9,2,7};// 统计 5 的元素个数intcountcount_if(nums.begin(),nums.end(),GreaterThan(5));cout大于 5 的个数: countendl;// 4// 找出第一个 6 的元素autoitfind_if(nums.begin(),nums.end(),GreaterThan(6));if(it!nums.end()){cout第一个 6: *itendl;// 8}return0;}二、Lambda 表达式2.1 Lambda 基础语法C11 引入的 lambda 是匿名函数对象语法简洁[capture](parameters)-return_type{body};#includeiostreamusingnamespacestd;intmain(){// 最简单的 lambda无参数无返回值autohello[](){coutHello Lambdaendl;};hello();// 带参数和返回值autoadd[](inta,intb){returnab;};coutadd(3,4)endl;// 7// 显式返回类型autodivide[](doublea,doubleb)-double{if(b0)return0;returna/b;};coutdivide(10,4)endl;// 2.5return0;}2.2 捕获列表详解lambda 可以通过捕获列表访问外部变量#includeiostreamusingnamespacestd;intmain(){intbase10;intfactor2;// 值捕获 [base]拷贝外部变量autoaddBase[base](intx){returnxbase;};coutaddBase(5)endl;// 15// 引用捕获 [factor]使用引用可修改外部变量automultiply[factor](intx){returnx*factor;};factor3;coutmultiply(5)endl;// 15factor 已变// 全部值捕获 []autoallByValue[](intx){returnxbasefactor;};// 全部引用捕获 []autoallByRef[](intx){returnx*factor;};// 混合捕获 [base, factor]automixed[base,factor](intx){returnxbase*factor;};return0;}2.3 捕获方式对比捕获方式语法说明值捕获[base]拷贝lambda 内只读引用捕获[base]引用可修改外部全部值捕获[]所有用到的按值全部引用捕获[]所有用到的按引用混合[base, factor]分别指定全部例外[, factor]除 factor 外按值全部例外[, base]除 base 外按引用2.4 mutable值捕获也可修改#includeiostreamusingnamespacestd;intmain(){intcounter0;// 默认值捕获在 lambda 内只读autoincrement[counter]()mutable{counter;// mutable 允许修改副本returncounter;};coutincrement()endl;// 1副本coutincrement()endl;// 2副本继续cout外部 counter: counterendl;// 0外部不变// 引用捕获可以直接修改外部autoincrementRef[counter](){counter;returncounter;};coutincrementRef()endl;// 1外部改变cout外部 counter: counterendl;// 1return0;}三、Lambda 与算法实战3.1 排序中的 Lambda#includeiostream#includevector#includealgorithmusingnamespacestd;structStudent{string name;intscore;intage;};intmain(){vectorStudentstudents{{张三,88,20},{李四,92,19},{王五,88,22},{赵六,76,21}};// 按分数降序sort(students.begin(),students.end(),[](constStudenta,constStudentb){returna.scoreb.score;});// 按分数降序分数相同按年龄升序多字段排序sort(students.begin(),students.end(),[](constStudenta,constStudentb){if(a.score!b.score)returna.scoreb.score;returna.ageb.age;});for(constautos:students){couts.name s.score分 s.age岁endl;}return0;}3.2 Lambda 实现复杂逻辑#includeiostream#includevector#includealgorithmusingnamespacestd;intmain(){vectorintnums{1,2,3,4,5,6,7,8,9,10};intlower3,upper8;// 组合条件筛选 [lower, upper] 范围内的偶数autocountcount_if(nums.begin(),nums.end(),[lower,upper](intx){returnxlowerxupperx%20;});cout范围内偶数个数: countendl;// 34,6,8// 变换范围内的数乘以倍数intmultiplier10;vectorintresult;transform(nums.begin(),nums.end(),back_inserter(result),[lower,upper,multiplier](intx){if(xlowerxupper)returnx*multiplier;returnx;});cout变换结果: ;for(intx:result)coutx ;coutendl;return0;}四、std::function 与 std::bind4.1 std::function 通用函数包装std::function可以存储任何可调用对象函数指针、lambda、函数对象#includeiostream#includefunctionalusingnamespacestd;intadd(inta,intb){returnab;}classMultiplier{public:intfactor;Multiplier(intf):factor(f){}intoperator()(intx)const{returnx*factor;}};intmain(){// std::function 存储不同类型的可调用对象functionint(int,int)op1add;// 函数指针functionint(int,int)op2[](inta,intb){// lambdareturna-b;};functionint(int)op3Multiplier(5);// 函数对象coutop1(3,4)endl;// 7coutop2(10,3)endl;// 7coutop3(6)endl;// 30// 可重新赋值op1[](inta,intb){returna*b;};coutop1(3,4)endl;// 12return0;}4.2 std::function 作为参数#includeiostream#includefunctional#includevectorusingnamespacestd;// 把操作作为参数传入voidprocessVector(vectorintv,constfunctionvoid(int)operation){for(autox:v){operation(x);}}intmain(){vectorintnums{1,2,3,4};// 传入不同的操作processVector(nums,[](intx){x*2;});cout翻倍: ;for(intx:nums)coutx ;coutendl;// 2 4 6 8processVector(nums,[](intx){x1;});cout加一: ;for(intx:nums)coutx ;coutendl;// 3 5 7 9return0;}4.3 std::bind 绑定参数#includeiostream#includefunctionalusingnamespacestd;intpower(intbase,intexp){intresult1;for(inti0;iexp;i)result*base;returnresult;}intmain(){usingnamespacestd::placeholders;// 绑定第二个参数为 2平方函数autosquarebind(power,_1,2);coutsquare(5) square(5)endl;// 25// 绑定第一个参数为 22 的 n 次方autopowerOf2bind(power,2,_1);coutpowerOf2(10) powerOf2(10)endl;// 1024// 反转参数顺序autorevbind(power,_2,_1);coutrev(3, 2) rev(3,2)endl;// 82^3return0;}五、函数对象 vs Lambda 对比维度函数对象Lambda语法定义类代码长简洁状态成员变量保存捕获列表复用可多次实例化每次都是新类型调试有类型名匿名类型使用场景需要复用的逻辑一次性的局部逻辑六、实战配置的统计工具综合本篇知识用 std::function 实现可配置统计#includeiostream#includevector#includealgorithm#includefunctionalusingnamespacestd;classStatsAnalyzer{private:vectorintdata;public:voidaddData(constvectorintvalues){data.insert(data.end(),values.begin(),values.end());}// 通用过滤统计传入任意判断条件intcountIf(constfunctionbool(int)predicate)const{returncount_if(data.begin(),data.end(),predicate);}// 通用聚合传入任意累积规则intaggregate(intinit,constfunctionint(int,int)accumulator)const{intresultinit;for(intx:data){resultaccumulator(result,x);}returnresult;}// 通用转换返回新数据vectorinttransformData(constfunctionint(int)mapper)const{vectorintresult;transform(data.begin(),data.end(),back_inserter(result),mapper);returnresult;}};intmain(){StatsAnalyzer analyzer;analyzer.addData({1,2,3,4,5,6,7,8,9,10});// 不同条件统计cout偶数: analyzer.countIf([](intx){returnx%20;})endl;cout大于 5: analyzer.countIf([](intx){returnx5;})endl;// 不同聚合方式cout求和: analyzer.aggregate(0,[](inta,intb){returnab;})endl;cout求积: analyzer.aggregate(1,[](inta,intb){returna*b;})endl;// 不同转换autosquaresanalyzer.transformData([](intx){returnx*x;});cout平方: ;for(intx:squares)coutx ;coutendl;return0;}总结本篇讲解了函数对象带状态、与算法配合、Lambda 表达式的完整语法捕获列表、mutable、值捕获与引用捕获的区别、std::function 通用包装、std::bind 参数绑定并用可配置统计工具串联实战。重点掌握捕获列表的七种写法、mutable 的作用、std::function 的灵活用法、多字段排序的 lambda 写法。下一篇将讲解容器适配器与实用工具stack/queue/priority_queue、pair/tuple、chrono、random敬请期待