std::copy_if是最直接的条件筛选方式,不修改原容器、语义清晰;常见错误是目标迭代器未预留空间或重叠,需用back_inserter或预分配空间,谓词捕获注意生命周期,就地过滤应选remove_if+erase。

std::copy_if 是最直接的实现方式
不用手写循环,std::copy_if 就是标准库为这类需求准备的工具。它把满足谓词的元素复制到目标区间,不修改原数组,语义清晰、无副作用。
常见错误是传错目标迭代器:比如用 std::back_inserter 时忘了提前预留空间,或直接用原数组起始地址导致越界写入。
- 源容器和目标容器不能重叠(除非用
std::remove_copy_if配合 erase-erase 惯用法) - 目标容器需有足够空间,或使用支持动态增长的容器 +
std::back_inserter - 谓词可为 lambda、函数指针或函数对象,捕获变量时注意生命周期
std::vector<int> src = {1, 2, 3, 4, 5, 6};
std::vector<int> dst;
dst.reserve(src.size()); // 避免多次 realloc
std::copy_if(src.begin(), src.end(), dst.begin(),
[](int x) { return x % 2 == 0; }); // 保留偶数
就地过滤要用 std::remove_if + erase 组合
如果必须复用原数组内存、不额外分配空间,std::remove_if 是唯一安全的就地方案。它不真正删除元素,而是把不满足谓词的元素“推到末尾”,返回新逻辑结尾——必须配合 erase 才能缩容。
漏掉 erase 是高频错误,会导致容器 size 不变、尾部残留无效数据;反过来,对普通数组(非容器)不能用 erase,得自己记录新长度。
立即学习“C++免费学习笔记(深入)”;
-
std::remove_if是稳定算法,相对顺序不变 - 对
std::array或裸数组,只能用返回的“新尾迭代器”标记有效范围,无法自动缩容 - 谓词为
true表示“要移除”,逻辑容易反,建议注释清楚
std::vector<int> v = {1, 2, 3, 4, 5};
v.erase(std::remove_if(v.begin(), v.end(),
[](int x) { return x < 3; }), // 移除小于 3 的
v.end());
// v 现在是 {3, 4, 5}
原始数组(int arr[])怎么处理
没有容器接口,就不能直接用 std::copy_if 写入原数组——会覆盖后续元素。正确做法是用两个指针手动遍历,或借助 std::partition(如果允许重排)。
最容易踩的坑是边界判断出错:比如循环条件写成 i <= len 导致访问越界,或新长度没保存下来,调用方仍按原长度使用。
- 返回新长度比修改原数组更安全,调用方决定是否 memcpy 或截断
- 若需保序且不能扩容,只能 O(n) 扫描两遍:第一遍计数,第二遍复制
-
std::partition效率高但打乱顺序,仅适用于“只要结果集、不要原始次序”的场景
int arr[] = {1, 2, 3, 4, 5};
int len = 5;
int write_idx = 0;
for (int i = 0; i < len; ++i) {
if (arr[i] % 2 == 0) { // 保留偶数
arr[write_idx++] = arr[i];
}
}
// 新有效长度是 write_idx,arr[0..write_idx) 是过滤后结果
性能与谓词开销需要注意什么
谓词本身不该有副作用,也不该做重操作(如文件读写、锁竞争)。编译器通常能内联简单 lambda,但若谓词调用虚函数或通过函数指针传入,可能阻碍优化。
对超大数组,缓存友好性比算法复杂度更关键:std::copy_if 是单向扫描,局部性好;而 std::remove_if 在某些实现中会有两次遍历(一次找、一次搬),但实际差异极小。
- 避免在谓词里做动态内存分配(如构造
std::string) - 若谓词逻辑复杂且被频繁调用,考虑提前计算并缓存中间结果
- 对
std::vector<bool>这种特化容器,std::copy_if可能因位操作产生意外行为,建议换用std::vector<char>


















