贝利信息

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

日期:2026-01-09 00:00 / 作者:尼克
std::ranges::sort(v)更安全清晰,直接传容器;视图组合filter/transform惰性高效;erase_if一步删除符合条件元素。

std::ranges::sort 替代 std::sort,直接传容器而非迭代器

传统 std::sort 必须传入 begin()end() 迭代器,容易写错边界或搞混顺序;std::ranges::sort 接收整个范围(如 std::vectorstd::array),语义更清晰,也自动适配自定义范围。

常见错误:混用旧式迭代器和 ranges 算法,比如对 std::vector 调用 std::ranges::sort(v.begin(), v.end()) —— 这会退化为传统行为,失去 ranges 的优势,且可能因迭代器类型不匹配编译失败。

std::vector v = {3, 1, 4, 1, 5};
std::ranges::sort(v); // ✅ 直接、安全、可读
// v 变为 {1, 1, 3, 4, 5}

std::views::filterstd::views::transform 替代手写循环

传统方式要开新容器、遍历、条件判断、push_back……容易漏 reserve、误用 ===、索引越界。Ranges 视图是惰性求值的,组合起来不产生中间容器,内存友好,逻辑也更接近自然语言。

关键点:视图对象本身不拥有数据,不能脱离原容器生命周期使用;一旦原容器被移动或销毁,再访问视图会 UB。

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::vectorstd::deque),不接受任意范围(如视图)。若你手头是个视图,得先确认它背后是否可修改且支持擦除。

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 组合后存为成员变量。

// ❌ 危险: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; })); }