引言在前面的 STL 系列中我们多次用到greaterint()、lessint()这样的写法它们就是仿函数Functor。仿函数不是函数而是重载了operator()的类对象可以像函数一样被调用。STL 内置了大量开箱即用的仿函数分为算术运算、关系比较、逻辑运算三大类。它们广泛用于sort、priority_queue、set/map等需要自定义行为的场景。第一部分仿函数基础一、为什么需要仿函数// 普通函数不能保存状态 bool cmp(int a, int b) { return a b; } // 仿函数可以保存状态 struct Cmp { int threshold; Cmp(int t) : threshold(t) {} bool operator()(int a, int b) const { return (a threshold) (b threshold); } }; Cmp cmp(10); cout cmp(15, 12) endl; // true cout cmp(5, 3) endl; // false对比函数指针仿函数Lambda保存状态❌✅✅内联优化❌ 难✅✅类型明确弱✅ 强匿名C 版本所有所有C11第二部分STL 内置仿函数STL 内置仿函数都在functional头文件中。一、算术类仿函数仿函数作用示例plusT加法plusint()(3,5)→ 8minusT减法minusint()(5,3)→ 2multipliesT乘法multipliesint()(3,5)→ 15dividesT除法dividesint()(6,3)→ 2modulusT取模modulusint()(7,3)→ 1negateT取反单目negateint()(5)→ -5#include iostream #include functional using namespace std; int main() { plusint add; cout add(3, 5) endl; // 8 cout plusint()(10, 20) endl; // 30匿名对象 negateint neg; cout neg(5) endl; // -5 modulusint mod; cout mod(10, 3) endl; // 110 % 3 return 0; }二、关系类仿函数仿函数作用示例equal_toT等于equal_toint()(3,3)→ truenot_equal_toT不等于!not_equal_toint()(3,5)→ truegreaterT大于greaterint()(5,3)→ truegreater_equalT大于等于greater_equalint()(5,5)→ truelessT小于lessint()(3,5)→ trueless_equalT小于等于less_equalint()(3,3)→ true#include iostream #include functional #include set #include vector #include algorithm using namespace std; int main() { // 1. sort 降序排列 vectorint v {3, 1, 4, 1, 5, 9}; sort(v.begin(), v.end(), greaterint()); // 9 5 4 3 1 1 // 2. set 降序 setint, greaterint s {3, 1, 4, 1, 5}; // 5 4 3 1 // 3. priority_queue 小顶堆 priority_queueint, vectorint, greaterint pq; return 0; }三、逻辑类仿函数仿函数作用示例logical_andT逻辑与logical_andbool()(true,false)→ falselogical_orT逻辑或||logical_orbool()(true,false)→ truelogical_notT逻辑非!logical_notbool()(true)→ false#include iostream #include functional #include vector #include algorithm using namespace std; int main() { vectorbool flags {true, false, true, true}; // 统计 true 的数量 int cnt count(flags.begin(), flags.end(), true); cout cnt endl; // 3 // 统计 false 的数量用 logical_not 反转 cnt count_if(flags.begin(), flags.end(), logical_notbool()); cout cnt endl; // 1 return 0; }第三部分常用场景一、sort 排序#include algorithm #include vector #include functional using namespace std; vectorint v {5, 2, 8, 1, 9}; sort(v.begin(), v.end()); // 默认 less升序 sort(v.begin(), v.end(), lessint()); // 升序等价 sort(v.begin(), v.end(), greaterint()); // 降序二、set/map 自定义排序// 降序 set setint, greaterint s; // 降序 map mapint, string, greaterint m;三、priority_queue 优先级// 大顶堆默认less priority_queueint pq1; // top 是最大值 // 小顶堆 priority_queueint, vectorint, greaterint pq2; // top 是最小值四、transform 变换#include algorithm #include vector #include functional using namespace std; vectorint v1 {1, 2, 3, 4, 5}; vectorint v2(v1.size()); // 每个元素取反 transform(v1.begin(), v1.end(), v2.begin(), negateint()); // v2: -1 -2 -3 -4 -5 // 两个数组相加 transform(v1.begin(), v1.end(), v2.begin(), v2.begin(), plusint()); // v2: 0 0 0 0 0第四部分自定义仿函数一、简单自定义// 自定义比较按绝对值排序 struct AbsCmp { bool operator()(int a, int b) const { return abs(a) abs(b); } }; vectorint v {-5, 2, -8, 1, 9}; sort(v.begin(), v.end(), AbsCmp()); // 1 2 -5 -8 9二、带状态的仿函数// 删除小于阈值的元素 class LessThan { private: int threshold; public: LessThan(int t) : threshold(t) {} bool operator()(int x) const { return x threshold; } }; vectorint v {5, 2, 8, 1, 9, 3}; // 删除小于 5 的元素 v.erase(remove_if(v.begin(), v.end(), LessThan(5)), v.end()); // 5 8 9三、仿函数适配器传统写法了解即可// C11 之前用 bind2nd 绑定第二个参数已废弃 // 现代 C 建议用 Lambda 替代 vectorint v {5, 2, 8, 1, 9, 3}; auto it find_if(v.begin(), v.end(), bind2nd(greaterint(), 5)); // 找第一个大于 5 的元素 // 现代写法推荐 auto it2 find_if(v.begin(), v.end(), [](int x) { return x 5; });第五部分仿函数 vs Lambda// 需求找第一个大于 5 的元素 // 方式1仿函数 struct GreaterThan5 { bool operator()(int x) const { return x 5; } }; auto it1 find_if(v.begin(), v.end(), GreaterThan5()); // 方式2LambdaC11推荐 auto it2 find_if(v.begin(), v.end(), [](int x) { return x 5; });对比仿函数Lambda可复用性✅ 定义一次随处用❌ 匿名不能复用类型明确✅ 有类名❌ 匿名类型简洁性❌ 需要定义类✅ 一行搞定适用场景多处复用的逻辑一次性使用的逻辑推荐原则需要多处复用的逻辑如greaterint→ 仿函数一次性使用的逻辑 → Lambda需要保存复杂状态→ 仿函数总结一、内置仿函数速查类别仿函数用途算术plusminusmultipliesdividesmodulusnegate算术运算关系equal_tonot_equal_togreatergreater_equallessless_equal比较排序逻辑logical_andlogical_orlogical_not逻辑操作二、常用位置sort(v.begin(), v.end(), greaterint()); // 排序 setint, greaterint; // 有序容器 priority_queueint, vectorint, greaterint; // 优先队列 transform(v1.begin(), v1.end(), v2.begin(), negateint()); // 变换三、一句话记忆STL 内置仿函数在functional中分算术、关系、逻辑三类。它们本质是重载了operator()的类对象主要用于sort、set/map、priority_queue、transform等需要自定义行为的场景。可复用逻辑用仿函数一次性用 Lambda。
C++ STL 仿函数完全指南:从内置仿函数到自定义实现
引言在前面的 STL 系列中我们多次用到greaterint()、lessint()这样的写法它们就是仿函数Functor。仿函数不是函数而是重载了operator()的类对象可以像函数一样被调用。STL 内置了大量开箱即用的仿函数分为算术运算、关系比较、逻辑运算三大类。它们广泛用于sort、priority_queue、set/map等需要自定义行为的场景。第一部分仿函数基础一、为什么需要仿函数// 普通函数不能保存状态 bool cmp(int a, int b) { return a b; } // 仿函数可以保存状态 struct Cmp { int threshold; Cmp(int t) : threshold(t) {} bool operator()(int a, int b) const { return (a threshold) (b threshold); } }; Cmp cmp(10); cout cmp(15, 12) endl; // true cout cmp(5, 3) endl; // false对比函数指针仿函数Lambda保存状态❌✅✅内联优化❌ 难✅✅类型明确弱✅ 强匿名C 版本所有所有C11第二部分STL 内置仿函数STL 内置仿函数都在functional头文件中。一、算术类仿函数仿函数作用示例plusT加法plusint()(3,5)→ 8minusT减法minusint()(5,3)→ 2multipliesT乘法multipliesint()(3,5)→ 15dividesT除法dividesint()(6,3)→ 2modulusT取模modulusint()(7,3)→ 1negateT取反单目negateint()(5)→ -5#include iostream #include functional using namespace std; int main() { plusint add; cout add(3, 5) endl; // 8 cout plusint()(10, 20) endl; // 30匿名对象 negateint neg; cout neg(5) endl; // -5 modulusint mod; cout mod(10, 3) endl; // 110 % 3 return 0; }二、关系类仿函数仿函数作用示例equal_toT等于equal_toint()(3,3)→ truenot_equal_toT不等于!not_equal_toint()(3,5)→ truegreaterT大于greaterint()(5,3)→ truegreater_equalT大于等于greater_equalint()(5,5)→ truelessT小于lessint()(3,5)→ trueless_equalT小于等于less_equalint()(3,3)→ true#include iostream #include functional #include set #include vector #include algorithm using namespace std; int main() { // 1. sort 降序排列 vectorint v {3, 1, 4, 1, 5, 9}; sort(v.begin(), v.end(), greaterint()); // 9 5 4 3 1 1 // 2. set 降序 setint, greaterint s {3, 1, 4, 1, 5}; // 5 4 3 1 // 3. priority_queue 小顶堆 priority_queueint, vectorint, greaterint pq; return 0; }三、逻辑类仿函数仿函数作用示例logical_andT逻辑与logical_andbool()(true,false)→ falselogical_orT逻辑或||logical_orbool()(true,false)→ truelogical_notT逻辑非!logical_notbool()(true)→ false#include iostream #include functional #include vector #include algorithm using namespace std; int main() { vectorbool flags {true, false, true, true}; // 统计 true 的数量 int cnt count(flags.begin(), flags.end(), true); cout cnt endl; // 3 // 统计 false 的数量用 logical_not 反转 cnt count_if(flags.begin(), flags.end(), logical_notbool()); cout cnt endl; // 1 return 0; }第三部分常用场景一、sort 排序#include algorithm #include vector #include functional using namespace std; vectorint v {5, 2, 8, 1, 9}; sort(v.begin(), v.end()); // 默认 less升序 sort(v.begin(), v.end(), lessint()); // 升序等价 sort(v.begin(), v.end(), greaterint()); // 降序二、set/map 自定义排序// 降序 set setint, greaterint s; // 降序 map mapint, string, greaterint m;三、priority_queue 优先级// 大顶堆默认less priority_queueint pq1; // top 是最大值 // 小顶堆 priority_queueint, vectorint, greaterint pq2; // top 是最小值四、transform 变换#include algorithm #include vector #include functional using namespace std; vectorint v1 {1, 2, 3, 4, 5}; vectorint v2(v1.size()); // 每个元素取反 transform(v1.begin(), v1.end(), v2.begin(), negateint()); // v2: -1 -2 -3 -4 -5 // 两个数组相加 transform(v1.begin(), v1.end(), v2.begin(), v2.begin(), plusint()); // v2: 0 0 0 0 0第四部分自定义仿函数一、简单自定义// 自定义比较按绝对值排序 struct AbsCmp { bool operator()(int a, int b) const { return abs(a) abs(b); } }; vectorint v {-5, 2, -8, 1, 9}; sort(v.begin(), v.end(), AbsCmp()); // 1 2 -5 -8 9二、带状态的仿函数// 删除小于阈值的元素 class LessThan { private: int threshold; public: LessThan(int t) : threshold(t) {} bool operator()(int x) const { return x threshold; } }; vectorint v {5, 2, 8, 1, 9, 3}; // 删除小于 5 的元素 v.erase(remove_if(v.begin(), v.end(), LessThan(5)), v.end()); // 5 8 9三、仿函数适配器传统写法了解即可// C11 之前用 bind2nd 绑定第二个参数已废弃 // 现代 C 建议用 Lambda 替代 vectorint v {5, 2, 8, 1, 9, 3}; auto it find_if(v.begin(), v.end(), bind2nd(greaterint(), 5)); // 找第一个大于 5 的元素 // 现代写法推荐 auto it2 find_if(v.begin(), v.end(), [](int x) { return x 5; });第五部分仿函数 vs Lambda// 需求找第一个大于 5 的元素 // 方式1仿函数 struct GreaterThan5 { bool operator()(int x) const { return x 5; } }; auto it1 find_if(v.begin(), v.end(), GreaterThan5()); // 方式2LambdaC11推荐 auto it2 find_if(v.begin(), v.end(), [](int x) { return x 5; });对比仿函数Lambda可复用性✅ 定义一次随处用❌ 匿名不能复用类型明确✅ 有类名❌ 匿名类型简洁性❌ 需要定义类✅ 一行搞定适用场景多处复用的逻辑一次性使用的逻辑推荐原则需要多处复用的逻辑如greaterint→ 仿函数一次性使用的逻辑 → Lambda需要保存复杂状态→ 仿函数总结一、内置仿函数速查类别仿函数用途算术plusminusmultipliesdividesmodulusnegate算术运算关系equal_tonot_equal_togreatergreater_equallessless_equal比较排序逻辑logical_andlogical_orlogical_not逻辑操作二、常用位置sort(v.begin(), v.end(), greaterint()); // 排序 setint, greaterint; // 有序容器 priority_queueint, vectorint, greaterint; // 优先队列 transform(v1.begin(), v1.end(), v2.begin(), negateint()); // 变换三、一句话记忆STL 内置仿函数在functional中分算术、关系、逻辑三类。它们本质是重载了operator()的类对象主要用于sort、set/map、priority_queue、transform等需要自定义行为的场景。可复用逻辑用仿函数一次性用 Lambda。