C++20 中的范围 for 与迭代器的改进

在 C++20 中,范围 for 循环得到了显著提升,主要体现在两个方面:① 对 std::ranges 的支持,② 对 std::spanstd::string_view 等视图类型的友好处理。本文将从这两个角度展开,帮助你在实际编码中充分利用新的语法特性。

1. 传统范围 for 与 C++20 的差异

传统的范围 for 语法:

for (auto &x : vec) {
    // 处理 x
}

它要求 vec 提供 begin()end() 成员或全局函数,并返回可比较的迭代器。C++20 通过 std::ranges::beginstd::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::beginstd::ranges::end,这些函数支持:

  • 传统容器(如 std::vectorstd::list
  • 视图(如 std::views::filter
  • 视图包装器(如 std::spanstd::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::filterstd::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_viewstd::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 ` 加入,即可享受到这些新特性带来的便利。祝你编码愉快!

发表评论