需包含头文件;返回difference_type类型值;常用Lambda谓词,注意char转unsigned char、捕获安全性、迭代器配对及const正确性;避免重复遍历,多条件宜合并统计。

std::count_if 的基本用法和必要头文件
要使用 std::count_if,必须包含 <algorithm></algorithm> 头文件,它不依赖 <numeric></numeric> 或 <iterator></iterator>(除非你手动传迭代器范围外的额外参数)。函数原型是:template<class inputit class unarypredicate> typename iterator_traits<inputit>::difference_type count_if(InputIt first, InputIt last, UnaryPredicate p);</inputit></class>。它返回满足谓词 p 为 true 的元素个数,类型是 difference_type(通常是 ptrdiff_t),不是 size_t,这点在比较或赋值时容易出错。
lambda 表达式是最常用也最安全的谓词写法
直接写函数对象或仿函数太重,而 lambda 能清晰表达意图且避免捕获错误。注意:谓词必须可调用、无副作用、且对同一输入始终返回相同结果(否则行为未定义)。
- 统计 vector 中偶数个数:
std::count_if(v.begin(), v.end(), [](int x) { return x % 2 == 0; }); - 统计字符串中大写字母:
std::count_if(s.begin(), s.end(), [](unsigned char c) { return std::isupper(c); });(注意char可能为负,传给std::isupper前应转unsigned char) - 捕获局部变量需谨慎:
int threshold = 10; std::count_if(v.begin(), v.end(), [threshold](int x) { return x > threshold; });—— 值捕获安全;若用引用捕获[&threshold],则必须确保threshold生命周期长于count_if调用。
常见误用:传错迭代器范围或忽略 const 正确性
std::count_if 不检查迭代器有效性,越界或空范围不会报错,但结果未定义。尤其要注意容器为空时 begin() == end() 是合法的,此时返回 0,没问题;但若误把 end() 当作有效元素访问就会崩溃。
- 错误写法:
std::count_if(v.data(), v.data() + v.size() + 1, pred)—— 多读一个字节,UB - 对 const 容器应使用 const_iterator:
const std::vector<int>& v = ...; std::count_if(v.cbegin(), v.cend(), pred);</int>,混用begin()和cend()可能编译失败 - 数组场景别忘传正确边界:
int arr[] = {1,2,3}; std::count_if(std::begin(arr), std::end(arr), pred);,不用手算长度
性能与替代方案:什么时候不该用 count_if
std::count_if 是单趟遍历,时间复杂度 O(n),没有隐藏开销。但如果你已经遍历容器做其他事,再单独调一次 count_if 就是两趟——这时应合并逻辑。另外,它不能提前退出(即使找到 100 个就足够),也没有并行版本(c++17 的 std::count_if 并行策略需显式指定执行策略,如 std::execution::par,且编译器/STL 实现支持才生效)。
立即学习“C++免费学习笔记(深入)”;
- 需要“是否存在至少一个满足条件”?用
std::any_of更语义清晰、可能更早退出 - 需要同时统计多个条件?别反复调
count_if,写一个循环一次分拣 - 对
std::list或其它非随机访问容器,count_if没问题,但不要期望它比手写循环快——它就是手写循环的泛化
实际用的时候,最常踩的坑是谓词里隐式类型转换(比如 char 传给 std::isxxx)、迭代器配对错误、以及把返回值当成 size_t 直接和 .size() 比较导致符号扩展警告。这些地方不报错,但运行时行为可能出人意料。