std::ranges::copy_if需包含和(或对应容器头文件),c++20起可用,接收源范围、目标迭代器和谓词,目标容器须预分配空间或用back_inserter,否则越界;谓词参数应匹配元素类型,推荐用[&](const auto& x)捕获;区别于std::copy_if在于直接接受range而非迭代器对。

std::ranges::copy_if 的基本用法和必要头文件
必须包含 <algorithm></algorithm> 和 <vector></vector>,C++20 起 std::ranges::copy_if 才可用,别用 std::copy_if 混淆——后者不接受 range(如 std::vector 直接传入),而前者支持。
它接收三个参数:源 range、目标迭代器、谓词函数。注意:目标容器(如 vec)需预先分配空间或用 std::back_inserter;否则写入越界。
- 错误写法:
std::vector<int> out; std::ranges::copy_if(src, out.begin(), pred);</int>——out为空,out.begin()无效 - 正确写法之一:
std::vector<int> out; out.reserve(src.size()); std::ranges::copy_if(src, std::back_inserter(out), pred);</int> -
std::back_inserter是最常用且安全的选择,自动调用push_back
谓词函数怎么写才不触发编译错误
谓词必须可调用、返回 bool,且参数类型要与 range 元素匹配。常见翻车点是捕获 lambda 中引用了已销毁对象,或用了非 const 引用参数却传入临时值。
例如过滤偶数:
std::vector<int> src = {1, 2, 3, 4, 5};
std::vector<int> out;
std::ranges::copy_if(src, std::back_inserter(out), [](int x) { return x % 2 == 0; });</int></int>
- 用
auto&&比int更泛化,尤其对复杂类型:[](const auto& x) { return x.active; } - 避免写成
[](auto& x)并在谓词里修改x——copy_if不承诺元素可变,且可能传入 const 视图 - 若谓词依赖外部变量,确保生命周期覆盖整个
copy_if调用,比如不要在循环内定义局部 lambda 然后存到 vector 里再用
和 std::copy_if 的关键区别在哪
核心差异在接口:前者接受 range(如 std::vector、std::span、任意满足 range 概念的类型),后者只认迭代器对(first, last)。
-
std::ranges::copy_if(v, inserter, pred)——v是容器本身 -
std::copy_if(v.begin(), v.end(), inserter, pred)—— 必须拆出迭代器 - 前者能自动适配
std::views::filter等 lazy view,但copy_if不能直接接 view(需先转为 range 或用begin/end) - 性能无本质差别,但
ranges版本更简洁、更易组合(比如链式调用src | std::views::filter(pred) | std::ranges::to<:vector></:vector>)
目标 vector 容量不足时会发生什么
如果没用 std::back_inserter,而是传入 out.begin() 且 out.size() ,行为是未定义(通常 crash 或内存破坏),不是抛异常。
- 安全做法只有两种:
std::back_inserter(out),或先out.resize(n)(n 可预估)再传out.begin() -
std::ranges::copy_if不会自动扩容目标容器——它只是按你给的迭代器一路写,不管后面有没有空间 - 若想复用已有 vector 并清空重填,记得先
out.clear(),否则新数据会追加到旧数据后
最简健壮写法就一句:std::vector<t> out; std::ranges::copy_if(src, std::back_inserter(out), pred);</t>。其他路径都得自己扛容量和生命周期责任。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











