跳转到主内容
极星编程网:以代码为星,赴技术山海!

如何用c++20的Ranges库简化算法操作? (代码实例)

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::vector v = {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++免费学习笔记(深入) ”; C函数速查手册(CHM版) C函数速查手册(CHM版) 下载
filter
接收一元谓词,返回满足条件的元素引用
transform
接收一元函数,返回转换后的新值(注意:若返回临时对象,需确保生命周期足够长) 链式调用顺序即执行顺序:
v | filter(...) | transform(...)
先过滤再转换 要落地为容器,必须显式构造,例如
std::vector(v | std::views::filter(...) | std::views::transform(...))
std::vector v = {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::vector v = {−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; })); }

相关文章