在 C++20 中,范围 for 循环得到了显著提升,主要体现在两个方面:① 对 std::ranges 的支持,② 对 std::span、std::string_view 等视图类型的友好处理。本文将从这两个角度展开,帮助你在实际编码中充分利用新的语法特性。
1. 传统范围 for 与 C++20 的差异
传统的范围 for 语法:
for (auto &x : vec) {
// 处理 x
}
它要求 vec 提供 begin() 与 end() 成员或全局函数,并返回可比较的迭代器。C++20 通过 std::ranges::begin 与 std::ranges::end 的改进,使得任何支持“视图”的类型都能直接参与范围 for,而无需显式地实现 begin/end。
2. std::ranges 的引入
2.1 视图(View)的概念
视图是一种惰性、可组合的容器包装器。常见的视图有:
std::views::filter:按条件过滤元素std::views::transform:按函数映射元素std::views::reverse:反向迭代std::views::take/drop:截取或跳过元素
通过这些视图,你可以在不复制数据的前提下,对容器进行链式操作。
#include <vector>
#include <iostream>
#include <ranges>
int main() {
std::vector <int> v{1, 2, 3, 4, 5, 6};
// 只取偶数并将其平方
for (auto x : v | std::views::filter([](int n){ return n % 2 == 0; })
| std::views::transform([](int n){ return n * n; })) {
std::cout << x << ' ';
}
// 输出: 4 16 36
}
2.2 范围 for 的改进
在 C++20 中,范围 for 内部会自动调用 std::ranges::begin 与 std::ranges::end,这些函数支持:
- 传统容器(如
std::vector、std::list) - 视图(如
std::views::filter) - 视图包装器(如
std::span、std::string_view)
这意味着你可以直接遍历 std::string_view:
std::string_view sv = "Hello, 世界!";
for (char c : sv) {
std::cout << c << ' ';
}
3. std::span 与范围 for
std::span 是一种“数组视图”,可以在不复制数据的前提下访问连续存储的元素。它本身实现了 begin()/end(),因此可以直接用范围 for:
std::array<int, 5> arr = {10, 20, 30, 40, 50};
std::span <int> sp(arr);
for (int val : sp) {
std::cout << val << ' ';
}
// 输出: 10 20 30 40 50
std::span 的优势在于:
- 可用于 C 风格数组、
std::vector的子范围、std::array等 - 可直接传递给函数,避免不必要的拷贝
- 与
std::ranges兼容,可在视图链中使用
4. 常见使用场景
4.1 迭代子数组
假设你只想处理数组的一部分:
int data[100];
std::span <int> whole(data);
std::span <int> part = whole.subspan(10, 20); // 从索引 10 开始,长度 20
for (int x : part) {
// 处理
}
4.2 过滤字符串
利用 std::views::filter 与 std::string_view,快速去除非字母字符:
std::string_view sv = "Hello, 123 World!";
for (char c : sv | std::views::filter([](char ch){ return std::isalpha(ch); })) {
std::cout << c;
}
// 输出: HelloWorld
4.3 变形序列
用 std::views::transform 对序列进行变形:
std::vector <int> nums{1, 2, 3, 4, 5};
for (auto x : nums | std::views::transform([](int n){ return n * 2; })) {
std::cout << x << ' ';
}
// 输出: 2 4 6 8 10
5. 性能与安全性
- 惰性求值:视图链不立即执行,只有在遍历时才计算。避免不必要的中间容器。
- 只读保证:
std::string_view、std::span默认是只读的。若需要写入,请使用 `std::span ` 或 `std::string`。 - 范围检查:在 C++20 中
std::span不提供范围检查;若想要检查,可使用std::span<T, std::dynamic_extent>并结合std::experimental::as_const或自定义检查。
6. 小结
C++20 通过引入 std::ranges 与改进的 std::span,让范围 for 变得更加强大与灵活。你可以:
- 通过视图链式组合过滤、变形、截取等操作,而无需显式复制
- 在不复制数据的前提下,遍历子数组、字符串视图
- 利用惰性求值与只读保证,写出更安全、更高效的代码
在日常项目中,只需将 `#include
` 与 `#include ` 加入,即可享受到这些新特性带来的便利。祝你编码愉快!