C++20 为标准库引入了强大的范围(ranges)功能,彻底改变了我们处理容器、序列以及算法的方式。与传统的迭代器配合使用的 std::copy, std::transform 等函数相比,范围适配器更直观、更安全、更易组合。本文将系统梳理范围适配器的核心概念、常用工具以及实际开发中的最佳实践。
1. 范围(Range)概念回顾
在 C++20 之前,标准算法需要接受两种迭代器(begin 和 end)作为参数。范围是一种抽象,代表一系列可遍历的元素。C++20 用 std::ranges::range 抽象来描述任何可用作范围的类型,例如数组、std::vector、std::string、甚至是自定义的链表。
#include <vector>
#include <ranges>
std::vector <int> v{1, 2, 3, 4, 5};
static_assert(std::ranges::range<std::vector<int>>); // true
2. 视图(View)与适配器(Adaptor)
视图是对范围的惰性包装,类似管道化的函数链。常见的视图包括:
| 视图 | 作用 | 语法示例 |
|---|---|---|
std::views::filter |
根据谓词过滤元素 | v | std::views::filter([](int n){ return n%2==0; }) |
std::views::transform |
对元素应用变换 | v | std::views::transform([](int n){ return n*n; }) |
std::views::reverse |
反转序列 | v | std::views::reverse |
std::views::take / std::views::drop |
取前 N / 跳过前 N 个元素 | v | std::views::take(3) |
视图是延迟求值的:直到真正遍历时才执行,避免了临时容器的拷贝,提升性能。
2.1 组合视图
视图可组合成复杂的管道:
#include <iostream>
auto even_squares = v
| std::views::filter([](int n){ return n % 2 == 0; })
| std::views::transform([](int n){ return n * n; });
for (int x : even_squares)
std::cout << x << ' ';
输出:4 16。
3. 算法与视图的无缝融合
C++20 还将算法改为 基于范围 的形式,直接接受范围对象,而不需要显式传递迭代器。例如:
#include <algorithm>
#include <numeric>
#include <ranges>
#include <iostream>
int main() {
std::vector <int> numbers{1, 2, 3, 4, 5};
// 计算平方后的和
int sum = std::ranges::accumulate(
numbers | std::views::transform([](int n){ return n * n; }),
0
);
std::cout << "Sum of squares: " << sum << '\n';
}
4. 范围适配器的优势
| 优势 | 传统方式 | C++20 范围方式 |
|---|---|---|
| 表达式简洁 | std::copy_if(v.begin(), v.end(), back_inserter(result), pred); |
std::ranges::copy_if(v, back_inserter(result), pred); |
| 惰性求值 | 需要临时容器 | 视图自动延迟 |
| 类型安全 | 迭代器易错 | 范围检查 |
| 链式调用 | 受限 | 可组合视图 |
| 可读性 | 较差 | 接近数学符号 |
5. 实战技巧
-
避免过度视图
视图链过长会导致编译器生成的代码膨胀。对复杂逻辑,适当将中间结果保存为临时容器,既易读又能避免编译时间过长。 -
自定义视图
利用std::views::iota,std::views::keys,std::views::values等,结合自定义std::ranges::view_interface可以快速构建专属视图。 -
与并行算法配合
C++20 允许对范围使用std::execution::par,实现并行化:std::ranges::for_each( std::execution::par, v | std::views::transform([](int n){ return n * 2; }), [](int x){ std::cout << x << ' '; } );
6. 小结
C++20 的范围适配器为 C++ 带来了更接近函数式编程的表达方式。通过惰性视图和基于范围的算法,代码更简洁、类型更安全、性能更优。掌握视图组合、并行执行以及自定义视图的技巧,能显著提升日常开发效率与代码可维护性。欢迎在实际项目中大胆尝试,挖掘 C++20 范围的潜力。