提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
-
-
- 一、谓词(Predicate)的核心定义
-
- [1. 通俗理解](#1. 通俗理解)
- [2. 专业定义](#2. 专业定义)
- 二、谓词的3种常见形式(结合std::find_if/std::count_if)
-
- [1. 形式1:普通函数(函数指针)](#1. 形式1:普通函数(函数指针))
- [2. 形式2:函数对象(仿函数,Functor)](#2. 形式2:函数对象(仿函数,Functor))
- [3. 形式3:Lambda表达式(最推荐)](#3. 形式3:Lambda表达式(最推荐))
- 三、谓词的使用注意事项
- 总结
-
你希望系统回顾C++中"谓词(Predicate)"的核心概念,尤其是和std::find_if、std::count_if等算法配合使用的自定义谓词,搞清楚它的定义、类型、写法和使用场景,对吧?我会从"是什么→有哪些形式→怎么用→注意事项"一步步讲,结合之前的vector查找例子,让你快速捡回这部分知识。
一、谓词(Predicate)的核心定义
1. 通俗理解
谓词就是"判断条件"的封装------它是一个可调用的对象(函数、Lambda、仿函数等),接收指定数量的参数,返回一个布尔值(bool),用于告诉算法"这个元素是否满足条件"。
2. 专业定义
在C++标准库中,谓词(Predicate)是满足以下条件的可调用对象:
- 返回值可以隐式转换为
bool; - 对于相同的输入,必须返回相同的结果(无副作用,比如不修改参数、不依赖全局变量);
- 分为两类:
- 一元谓词 :接收1个参数 (比如
std::find_if/std::count_if用的就是这个); - 二元谓词 :接收2个参数 (比如
std::sort的自定义排序规则)。
- 一元谓词 :接收1个参数 (比如
二、谓词的3种常见形式(结合std::find_if/std::count_if)
和std::find_if/std::count_if配合的是一元谓词(因为算法会遍历每个元素,把元素作为唯一参数传给谓词),常见有3种写法,其中Lambda表达式是最推荐的(新手友好、简洁、无需额外定义)。
1. 形式1:普通函数(函数指针)
把判断逻辑写在普通函数里,将函数名作为谓词传给算法(本质是传函数指针)。
示例(查找vector中年龄>25的Person):
cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
// 自定义类型
struct Person {
std::string name;
int age;
};
// ① 定义一元谓词函数(接收1个Person参数,返回bool)
bool isAgeGreaterThan25(const Person& p) {
return p.age > 25;
}
int main() {
std::vector<Person> people = {{"Alice", 20}, {"Bob", 28}, {"Charlie", 30}};
// ② 把函数名作为谓词传给std::find_if
auto it = std::find_if(people.begin(), people.end(), isAgeGreaterThan25);
if (it != people.end()) {
std::cout << "找到年龄>25的人:" << it->name << std::endl; // 输出:Bob
}
// 同理,传给std::count_if统计数量
int cnt = std::count_if(people.begin(), people.end(), isAgeGreaterThan25);
std::cout << "年龄>25的人数:" << cnt << std::endl; // 输出:2
return 0;
}
缺点:
- 函数需要单独定义,代码分散;
- 无法灵活传递"参数"(比如想判断>30,就得再写一个
isAgeGreaterThan30函数)。
2. 形式2:函数对象(仿函数,Functor)
重载()运算符的类/结构体,本质是"可以像函数一样调用的对象",支持携带自定义参数(解决普通函数的缺点)。
示例(可自定义阈值的年龄判断):
cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
struct Person {
std::string name;
int age;
};
// ① 定义函数对象(仿函数)
struct IsAgeGreaterThan {
int threshold; // 携带的自定义阈值(比如25、30)
// 构造函数:初始化阈值
IsAgeGreaterThan(int t) : threshold(t) {}
// 重载()运算符,实现一元谓词逻辑
bool operator()(const Person& p) const {
return p.age > threshold;
}
};
int main() {
std::vector<Person> people = {{"Alice", 20}, {"Bob", 28}, {"Charlie", 30}};
// ② 创建函数对象(指定阈值25),传给find_if
auto it = std::find_if(people.begin(), people.end(), IsAgeGreaterThan(25));
std::cout << "年龄>25的人:" << it->name << std::endl; // Bob
// ③ 换个阈值30,统计数量
int cnt = std::count_if(people.begin(), people.end(), IsAgeGreaterThan(30));
std::cout << "年龄>30的人数:" << cnt << std::endl; // 0
return 0;
}
优点 :支持携带自定义参数,复用性强;
缺点:需要定义类/结构体,代码稍繁琐(C++11后被Lambda替代)。
3. 形式3:Lambda表达式(最推荐)
C++11引入的匿名函数,可直接写在算法参数位置,简洁、灵活,还能捕获外部变量(完美替代前两种形式)。
语法回顾:
cpp
// 完整语法(常用简化版)
[capture](parameters) -> return_type { body }
// 简化版(auto推导返回值,无参数时()可省)
[捕获列表](参数列表) { 函数体 }
核心:捕获列表(Lambda能访问外部变量的关键):
| 捕获方式 | 说明 |
|---|---|
[] |
不捕获任何外部变量 |
[=] |
按值捕获所有外部变量(只读) |
[&] |
按引用捕获所有外部变量(可修改) |
[x] |
按值捕获变量x |
[&x] |
按引用捕获变量x |
示例(Lambda作为谓词,灵活传参):
cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
struct Person {
std::string name;
int age;
};
int main() {
std::vector<Person> people = {{"Alice", 20}, {"Bob", 28}, {"Charlie", 30}};
int threshold = 25; // 外部变量
// ① 无捕获:固定阈值
auto it1 = std::find_if(people.begin(), people.end(), [](const Person& p) {
return p.age > 25; // 直接写死阈值
});
// ② 按值捕获threshold:灵活调整阈值
auto it2 = std::find_if(people.begin(), people.end(), [threshold](const Person& p) {
return p.age > threshold; // 使用捕获的外部变量
});
// ③ 统计:按引用捕获(演示,这里不需要修改)
int cnt = std::count_if(people.begin(), people.end(), [&threshold](const Person& p) {
return p.age > threshold;
});
std::cout << "年龄>" << threshold << "的人:" << it2->name << std::endl; // Bob
std::cout << "年龄>" << threshold << "的人数:" << cnt << std::endl; // 2
return 0;
}
优点:
- 代码内联,无需额外定义函数/类;
- 支持捕获外部变量,灵活调整判断条件;
- 编译器优化好,性能和前两种一致;
- 是C++11后自定义谓词的首选方式。
三、谓词的使用注意事项
- 无副作用 :谓词不能修改传入的参数(建议参数用
const,比如const Person& p),也不能修改外部状态(比如捕获的变量尽量只读),否则会导致算法行为异常; - 返回值必须是bool :即使返回
int(比如return 1),也会隐式转换为bool,但建议显式返回bool,代码更清晰; - 传参效率 :对于自定义类型(如
Person),谓词参数建议用const引用 (const Person&),避免拷贝开销; - Lambda捕获 :
- 不需要修改外部变量时,优先按值捕获(
[=]或[x]); - 需要修改时,按引用捕获(
[&]或[&x]),但要确保变量生命周期长于算法执行周期。
- 不需要修改外部变量时,优先按值捕获(
总结
- 谓词核心 :是返回bool的可调用对象,
std::find_if/std::count_if用的是一元谓词(接收1个参数); - 常用形式 :优先用Lambda表达式(简洁、灵活),无需修改类/结构体,是自定义谓词的最佳选择;
- 关键注意 :谓词要无副作用,自定义类型参数用
const&,Lambda捕获按需选择值/引用。