std::count_if可高效统计满足条件的元素个数,时间复杂度O(n),适用于所有标准容器;需传入左闭右开区间和纯一元谓词,避免迭代器失效与谓词副作用。

直接用 std::count_if,配合 lambda 或函数对象即可统计满足条件的元素个数;它不修改容器,只遍历一次,时间复杂度是 O(n),且对所有标准容器(vector、list、array 等)通用。
std::count_if 的基本用法和参数含义
std::count_if 定义在 头文件中,签名是:
templatetypename iterator_traits ::difference_type count_if(InputIt first, InputIt last, UnaryPredicate p);
关键点:
-
first和last是左闭右开区间,即[first, last) -
p是一元谓词(返回bool的可调用对象),对每个元素调用p(value) - 返回满足
p(value) == true的元素个数,类型是iterator_traits<...>::difference_type(通常是int或long) - 注意:
last不能是end()的非法副本(比如 vector 移动后原迭代器失效)
常见写法:lambda 表达式最常用
90% 场景下用 lambda 最简洁。例如统计 vector 中偶数个数:
立即学习“C++免费学习笔记(深入)”;
std::vectorv = {1, 2, 3, 4, 5, 6}; auto even_count = std::count_if(v.begin(), v.end(), [](int x) { return x % 2 == 0; }); // 结果是 3
其他典型场景:
- 字符串中大写字母个数:
[](char c) { return std::isupper(static_cast(注意(c)); } std::isupper要求非负) - 指针容器中非空指针个数:
[](const T* p) { return p != nullptr; } - 自定义结构体中某字段满足条件:
[](const Person& p) { return p.age > 18; }
容易踩的坑:迭代器失效与谓词副作用
两个高频出错点:
-
传入已失效的迭代器:比如在
std::vector上调用push_back后未更新begin()/end(),再传给count_if→ 行为未定义 -
谓词有副作用或状态依赖:例如在 lambda 中修改外部变量、调用随机函数、或依赖全局计数器 → 结果不可预测,因为
count_if不保证遍历顺序(虽实际通常按序,但标准不保证) - 对
std::list或std::forward_list使用std::distance(begin(), end())模拟 count_if?别这么做——效率低(O(n²)),且逻辑冗余
替代方案对比:什么时候不该用 count_if?
如果需要同时做「统计」+「其他操作」(如收集匹配元素、提前退出、或需索引),count_if 就不够用了:
- 要获取所有匹配项 → 改用
std::copy_if+back_inserter - 只需知道是否存在满足条件的元素 → 用
std::any_of(更语义清晰,可能短路) - 条件判断成本极高,且预期多数不满足 → 手写循环 +
break可能更高效(但先 profile) - 统计后立刻要 erase 满足条件的元素 → 考虑
std::remove_if+erase(避免两次遍历)
真正要注意的是:谓词必须是纯函数(无副作用、无外部状态),否则结果可能在不同编译器或优化级别下不一致 —— 这点比语法细节更容易被忽略。











