std::ranges::sort(v)更安全清晰,直接传容器;视图组合filter/transform惰性高效;erase_if一步删除符合条件元素。

用 std::ranges::sort 替代 std::sort,直接传容器而非迭代器
传统 std::sort 必须传入 begin() 和 end() 迭代器,容易写错边界或搞混顺序;std::ranges::sort 接收整个范围(如 std::vector、std::array),语义更清晰,也自动适配自定义范围。
常见错误:混用旧式迭代器和 ranges 算法,比如对 std::vector 调用 std::ranges::sort(v.begin(), v.end()) —— 这会退化为传统行为,失去 ranges 的优势,且可能因迭代器类型不匹配编译失败。
- 正确写法:直接传容器对象,
std::ranges::sort(v) - 支持自定义比较:
std::ranges::sort(v, std::greater{}) - 仅对子范围排序:用
std::ranges::subrange或切片视图,例如std::ranges::sort(v | std::views::take(5))(注意:这会排序前 5 个元素的副本,需用std::views::take+std::ranges::sort配合可写视图,实际中更推荐std::ranges::sort(std::ranges::subrange(v.begin(), v.begin() + 5)))
std::vectorv = {3, 1, 4, 1, 5}; std::ranges::sort(v); // ✅ 直接、安全、可读 // v 变为 {1, 1, 3, 4, 5}
用 std::views::filter 和 std::views::transform 替代手写循环
传统方式要开新容器、遍历、条件判断、push_back……容易漏 reserve、误用 == 和 =、索引越界。Ranges 视图是惰性求值的,组合起来不产生中间容器,内存友好,逻辑也更接近自然语言。
关键点:视图对象本身不拥有数据,不能脱离原容器生命周期使用;一旦原容器被移动或销毁,再访问视图会 UB。
立即学习“C++免费学习笔记(深入)”;
-
filter接收一元谓词,返回满足条件的元素引用 -
transform接收一元函数,返回转换后的新值(注意:若返回临时对象,需确保生命周期足够长) - 链式调用顺序即执行顺序:
v | filter(...) | transform(...)先过滤再转换 - 要落地为容器,必须显式构造,例如
std::vector(v | std::views::filter(...) | std::views::transform(...))
std::vectorv = {1, 2, 3, 4, 5, 6}; auto evens_squared = v | std::views::filter([](int x) { return x % 2 == 0; }) | std::views::transform([](int x) { return x * x; }); std::vector
result(evens_squared.begin(), evens_squared.end()); // result 为 {4, 16, 36}
用 std::ranges::find_if + std::ranges::erase 安全删除满足条件的元素
旧式 remove_if + erase(erase–remove 惯用法)易出错:忘记 erase、迭代器失效、对 list 等非连续容器效率低。C++20 提供 std::ranges::erase(针对容器)和 std::ranges::erase_if(直接按条件删),语义明确、一步到位。
注意:std::ranges::erase_if 是容器专属算法,只接受容器(如 std::vector、std::deque),不接受任意范围(如视图)。若你手头是个视图,得先确认它背后是否可修改且支持擦除。
std::ranges::erase_if(v, [](int x) { return x 删除所有负数- 返回值是被删元素个数,可用于断言或日志
- 对
std::vector,内部仍做移动,但接口屏蔽了细节;对std::list,是真正的 O(n) 删除
std::vectorv = {−2, 3, −1, 7, 0, −5}; auto n = std::ranges::erase_if(v, [](int x) { return x < 0; }); // v 变为 {3, 7, 0},n == 3
组合视图时小心求值时机和临时对象生命周期
这是最常被忽略的坑:视图是轻量级对象,但其内部可能绑定临时容器或 lambda 捕获。一旦绑定源离开作用域,继续使用视图就是悬垂引用。
典型错误场景:函数返回一个由局部容器生成的视图、在 lambda 中捕获局部变量后用于 transform、把 std::views::iota 和临时 vector 组合后存为成员变量。
- 避免返回视图:改用返回
std::vector或接收输出迭代器 - lambda 捕获尽量用值捕获(
[x]),不用引用([&x])除非你能保证x生命周期覆盖视图使用期 - 调试技巧:对怀疑悬垂的视图,加一句
std::ranges::size(view)测试——若崩溃或返回异常大值,大概率是生命周期问题
// ❌ 危险:view 绑定到局部 vector,函数返回后失效
auto get_even_view() {
std::vector local = {1, 2, 3, 4};
return local | std::views::filter([](int x) { return x % 2 == 0; });
}
// ✅ 安全:立即物化为 vector
auto get_even_vec() {
std::vector local = {1, 2, 3, 4};
return std::vector(local | std::views::filter([](int x) { return x % 2 == 0; }));
}











