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捕获按需选择值/引用。
相关推荐
m0_736919109 小时前
C++代码风格检查工具
开发语言·c++·算法
2301_7634724611 小时前
C++20概念(Concepts)入门指南
开发语言·c++·算法
阿猿收手吧!11 小时前
【C++】std::promise原理与实战解析
c++
m0_7066532312 小时前
分布式系统安全通信
开发语言·c++·算法
Zach_yuan12 小时前
深入浅出 JSONCpp
linux·服务器·网络·c++
寻寻觅觅☆12 小时前
东华OJ-基础题-104-A == B ?(C++)
开发语言·c++
lightqjx13 小时前
【C++】unordered系列的封装
开发语言·c++·stl·unordered系列
阿猿收手吧!13 小时前
【C++】string_view:高效字符串处理指南
开发语言·c++
Word码14 小时前
[C++语法] 继承 (用法详解)
java·jvm·c++
lxl130714 小时前
C++算法(1)双指针
开发语言·c++