C++ STL 仿函数完全指南:从内置仿函数到自定义实现
引言
在前面的 STL 系列中,我们多次用到greater<int>()、less<int>()这样的写法,它们就是仿函数(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++ 版本 | 所有 | 所有 | C++11+ |
第二部分:STL 内置仿函数
STL 内置仿函数都在<functional>头文件中。
一、算术类仿函数
| 仿函数 | 作用 | 示例 |
|---|---|---|
plus<T> | 加法 | plus<int>()(3,5)→ 8 |
minus<T> | 减法 | minus<int>()(5,3)→ 2 |
multiplies<T> | 乘法 | multiplies<int>()(3,5)→ 15 |
divides<T> | 除法 | divides<int>()(6,3)→ 2 |
modulus<T> | 取模 | modulus<int>()(7,3)→ 1 |
negate<T> | 取反(单目) | negate<int>()(5)→ -5 |
#include <iostream> #include <functional> using namespace std; int main() { plus<int> add; cout << add(3, 5) << endl; // 8 cout << plus<int>()(10, 20) << endl; // 30(匿名对象) negate<int> neg; cout << neg(5) << endl; // -5 modulus<int> mod; cout << mod(10, 3) << endl; // 1(10 % 3) return 0; }二、关系类仿函数
| 仿函数 | 作用 | 示例 |
|---|---|---|
equal_to<T> | 等于== | equal_to<int>()(3,3)→ true |
not_equal_to<T> | 不等于!= | not_equal_to<int>()(3,5)→ true |
greater<T> | 大于> | greater<int>()(5,3)→ true |
greater_equal<T> | 大于等于>= | greater_equal<int>()(5,5)→ true |
less<T> | 小于< | less<int>()(3,5)→ true |
less_equal<T> | 小于等于<= | less_equal<int>()(3,3)→ true |
#include <iostream> #include <functional> #include <set> #include <vector> #include <algorithm> using namespace std; int main() { // 1. sort 降序排列 vector<int> v = {3, 1, 4, 1, 5, 9}; sort(v.begin(), v.end(), greater<int>()); // 9 5 4 3 1 1 // 2. set 降序 set<int, greater<int>> s = {3, 1, 4, 1, 5}; // 5 4 3 1 // 3. priority_queue 小顶堆 priority_queue<int, vector<int>, greater<int>> pq; return 0; }三、逻辑类仿函数
| 仿函数 | 作用 | 示例 |
|---|---|---|
logical_and<T> | 逻辑与&& | logical_and<bool>()(true,false)→ false |
logical_or<T> | 逻辑或|| | logical_or<bool>()(true,false)→ true |
logical_not<T> | 逻辑非! | logical_not<bool>()(true)→ false |
#include <iostream> #include <functional> #include <vector> #include <algorithm> using namespace std; int main() { vector<bool> 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_not<bool>()); cout << cnt << endl; // 1 return 0; }第三部分:常用场景
一、sort 排序
#include <algorithm> #include <vector> #include <functional> using namespace std; vector<int> v = {5, 2, 8, 1, 9}; sort(v.begin(), v.end()); // 默认 less,升序 sort(v.begin(), v.end(), less<int>()); // 升序,等价 sort(v.begin(), v.end(), greater<int>()); // 降序二、set/map 自定义排序
// 降序 set set<int, greater<int>> s; // 降序 map map<int, string, greater<int>> m;三、priority_queue 优先级
// 大顶堆(默认,less) priority_queue<int> pq1; // top 是最大值 // 小顶堆 priority_queue<int, vector<int>, greater<int>> pq2; // top 是最小值四、transform 变换
#include <algorithm> #include <vector> #include <functional> using namespace std; vector<int> v1 = {1, 2, 3, 4, 5}; vector<int> v2(v1.size()); // 每个元素取反 transform(v1.begin(), v1.end(), v2.begin(), negate<int>()); // v2: -1 -2 -3 -4 -5 // 两个数组相加 transform(v1.begin(), v1.end(), v2.begin(), v2.begin(), plus<int>()); // v2: 0 0 0 0 0第四部分:自定义仿函数
一、简单自定义
// 自定义比较:按绝对值排序 struct AbsCmp { bool operator()(int a, int b) const { return abs(a) < abs(b); } }; vector<int> 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; } }; vector<int> v = {5, 2, 8, 1, 9, 3}; // 删除小于 5 的元素 v.erase(remove_if(v.begin(), v.end(), LessThan(5)), v.end()); // 5 8 9三、仿函数适配器(传统写法,了解即可)
// C++11 之前用 bind2nd 绑定第二个参数(已废弃) // 现代 C++ 建议用 Lambda 替代 vector<int> v = {5, 2, 8, 1, 9, 3}; auto it = find_if(v.begin(), v.end(), bind2nd(greater<int>(), 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()); // 方式2:Lambda(C++11,推荐) auto it2 = find_if(v.begin(), v.end(), [](int x) { return x > 5; });| 对比 | 仿函数 | Lambda |
|---|---|---|
| 可复用性 | ✅ 定义一次,随处用 | ❌ 匿名,不能复用 |
| 类型明确 | ✅ 有类名 | ❌ 匿名类型 |
| 简洁性 | ❌ 需要定义类 | ✅ 一行搞定 |
| 适用场景 | 多处复用的逻辑 | 一次性使用的逻辑 |
推荐原则:
需要多处复用的逻辑(如
greater<int>)→ 仿函数一次性使用的逻辑 → Lambda
需要保存复杂状态→ 仿函数
总结
一、内置仿函数速查
| 类别 | 仿函数 | 用途 |
|---|---|---|
| 算术 | plusminusmultipliesdividesmodulusnegate | 算术运算 |
| 关系 | equal_tonot_equal_togreatergreater_equallessless_equal | 比较排序 |
| 逻辑 | logical_andlogical_orlogical_not | 逻辑操作 |
二、常用位置
sort(v.begin(), v.end(), greater<int>()); // 排序 set<int, greater<int>>; // 有序容器 priority_queue<int, vector<int>, greater<int>>; // 优先队列 transform(v1.begin(), v1.end(), v2.begin(), negate<int>()); // 变换三、一句话记忆
STL 内置仿函数在<functional>中,分算术、关系、逻辑三类。它们本质是重载了operator()的类对象,主要用于sort、set/map、priority_queue、transform等需要自定义行为的场景。可复用逻辑用仿函数,一次性用 Lambda。
