C++谓词

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录


你希望系统回顾C++中"谓词(Predicate)"的核心概念,尤其是和std::find_ifstd::count_if等算法配合使用的自定义谓词,搞清楚它的定义、类型、写法和使用场景,对吧?我会从"是什么→有哪些形式→怎么用→注意事项"一步步讲,结合之前的vector查找例子,让你快速捡回这部分知识。

一、谓词(Predicate)的核心定义

1. 通俗理解

谓词就是"判断条件"的封装------它是一个可调用的对象(函数、Lambda、仿函数等),接收指定数量的参数,返回一个布尔值(bool),用于告诉算法"这个元素是否满足条件"。

2. 专业定义

在C++标准库中,谓词(Predicate)是满足以下条件的可调用对象:

  • 返回值可以隐式转换为bool
  • 对于相同的输入,必须返回相同的结果(无副作用,比如不修改参数、不依赖全局变量);
  • 分为两类:
    • 一元谓词 :接收1个参数 (比如std::find_if/std::count_if用的就是这个);
    • 二元谓词 :接收2个参数 (比如std::sort的自定义排序规则)。

二、谓词的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后自定义谓词的首选方式

三、谓词的使用注意事项

  1. 无副作用 :谓词不能修改传入的参数(建议参数用const,比如const Person& p),也不能修改外部状态(比如捕获的变量尽量只读),否则会导致算法行为异常;
  2. 返回值必须是bool :即使返回int(比如return 1),也会隐式转换为bool,但建议显式返回bool,代码更清晰;
  3. 传参效率 :对于自定义类型(如Person),谓词参数建议用const引用const Person&),避免拷贝开销;
  4. Lambda捕获
    • 不需要修改外部变量时,优先按值捕获([=][x]);
    • 需要修改时,按引用捕获([&][&x]),但要确保变量生命周期长于算法执行周期。

总结

  1. 谓词核心 :是返回bool的可调用对象,std::find_if/std::count_if用的是一元谓词(接收1个参数);
  2. 常用形式 :优先用Lambda表达式(简洁、灵活),无需修改类/结构体,是自定义谓词的最佳选择;
  3. 关键注意 :谓词要无副作用,自定义类型参数用const&,Lambda捕获按需选择值/引用。
相关推荐
汉克老师3 小时前
GESP2025年9月认证C++五级真题与解析(单选题9-15)
c++·算法·贪心算法·排序算法·归并排序·gesp5级·gesp五级
飞鹰513 小时前
CUDA高级优化实战:Stream、特殊内存与卷积优化—Week3学习总结
c++·gpt·chatgpt·gpu算力
txinyu的博客5 小时前
std::function
服务器·开发语言·c++
学嵌入式的小杨同学5 小时前
【嵌入式 C 语言实战】交互式栈管理系统:从功能实现到用户交互全解析
c语言·开发语言·arm开发·数据结构·c++·算法·链表
txinyu的博客5 小时前
static_cast、const_cast、dynamic_cast、reinterpret_cast
linux·c++
“αβ”6 小时前
TCP相关实验
运维·服务器·网络·c++·网络协议·tcp/ip·udp
孞㐑¥6 小时前
算法—滑动窗口
开发语言·c++·经验分享·笔记·算法
一分之二~7 小时前
二叉树--求最小深度(迭代和递归)
数据结构·c++·算法·leetcode·深度优先
智者知已应修善业7 小时前
【输出一个N*N的01矩阵,表示最后的汉字点阵图】2024-10-22
c语言·数据结构·c++·经验分享·笔记·算法·矩阵