广告

C++20 Ranges库快速上手:如何用Ranges简化STL算法的革命性特性与实战指南

为何 C++20 Ranges 能改变 STL 的编程范式

核心思想与优势

在传统 STL 中,算法与容器的交互往往需要显式迭代器管理和临时容器,Ranges 引入的视图(views)将数据传输和处理解耦,从而使代码更接近自然语言表达。

通过将算法放在管道中连接,管道操作符(|)与视图组合 可以在不修改数据源的情况下实现惰性计算与高效内存使用。

这种方式带来的一个明显优势是 延迟求值、懒加载和更易组合,极大提升了可读性与可维护性。

常见误区与对比

很多开发者误以为 Ranges 只是一个包装,其实它改变了遍历语义,将“查看数据”与“处理数据”分离开来。

与传统 STL 相比,范围视图不是拷贝数据,而是对现有数据的轻量封装与组合,能显著减少冗余存储。

C++20 Ranges库快速上手:如何用Ranges简化STL算法的革命性特性与实战指南

快速上手:从零到初级应用

环境配置与编译要点

要启用 C++20 的 Ranges 功能,确保编译器支持 --std=c++20,并在某些实现上开启 libstdc++/libc++ 的 ranges 实现

在项目中引入 #include 头文件后,基本的 range 操作就可以使用,在代码中更关注“做什么”而非“如何遍历”。

需要留意的是 编译器对三方库的范围支持程度不同,若遇到问题可查看文档或切换到最新的工具链。

基础用法示例

下面展示一个简单的例子,使用 ranges 的管道将整型序列平方后筛选正数并打印:

#include <iostream>
#include <vector>
#include <ranges>int main() {std::vector<int> v = {-2, 0, 1, 2, 3};auto squares = v | std::views::transform([](int x){ return x * x; })| std::views::filter([](int x){ return x > 0; });for (int x : squares) {std::cout << x << ' ';}std::cout << '\\n';return 0;
}

这里的 transform 和 filter 都来自 ranges 的视图,返回的是惰性序列。

典型模式:视图、管道和算法的结合

视图的构建与组合

视图是范围的轻量代理,通过 composition 将多种操作拼接成流水线,例如使用 views::iotaviews::filterviews::transform 的组合。

组合的强大在于可重用性和可读性,不需要显式循环就能完成复杂数据处理

在实践中,推荐将视图和算法的职责分离,先构建视图,再对视图应用标准算法,这有助于提升可维护性。

算法适配与容器兼容

对 STL 算法而言,Ranges 提供了轻量的适配层,使 大多数算法可以直接对视图使用,如 std::ranges::sortstd::ranges::copy 等。

在某些场景下,需要使用 ranges::subrange 与 begin/end 的组合来实现自定义迭代逻辑,从而兼容现有容器。

下面给出一个综合示例,演示如何用 ranges 组合构建一个自定义管道并应用标准算法:

#include <algorithm>
#include <iostream>
#include <ranges>
#include <vector>int main() {std::vector<int> data = {5, 2, 9, 1, 7, 3};auto pipeline = data| std::views::filter([](int x){ return x % 2 == 1; })| std::views::transform([](int x){ return x * 3; });// 将结果拷贝到另一个容器并排序std::vector<int> out;std::ranges::copy(pipeline, std::back_inserter(out));std::ranges::sort(out);for (int x : out) std::cout << x << ' ';std::cout << '\\n';
}

使用 std::ranges::copy 和 std::ranges::sort 可以直接在视图之上完成高效操作。

示例实战:用 Ranges 重写常见 STL 算法

案例1:筛选与映射

在数据清洗中,通过 ranges 可以将筛选与映射步骤用管道串联,避免手写循环与临时容器

核心要点是保持惰性计算,直到最终需要结果时再触发求值,降低内存峰值

下面给出一个具体案例,先筛选正数再进行平方映射,然后收集结果:

#include <iostream>
#include <vector>
#include <ranges>int main() {std::vector<int> nums = {-3, -1, 0, 2, 4};auto result = nums| std::views::filter([](int x){ return x > 0; })| std::views::transform([](int x){ return x * x; });for (int x : result) std::cout << x << ' ';std::cout << '\\n';
}

打印结果展示了惰性管道的工作方式,只在遍历时才执行过滤和变换。

案例2:组合管道与写入

将 ranges 与 STL 的写入适配器结合,直接将管道结果写入输出容器或流,提升代码简洁度。

#include <vector>
#include <ranges>
#include <iostream>int main() {std::vector<int> a = {1,2,3,4,5};std::vector<int> b;std::ranges::copy(a | std::views::transform([](int x){ return x * 2; }),std::back_inserter(b));for (int x : b) std::cout << x << ' ';std::cout << '\\n';
}

输出向量 b 的内容等效于把管道结果落地到容器中,这是与 STL 直观使用对比的一个直观收益。

广告

后端开发标签