Skip to content

ranges — 范围库 C++20

C++20 Ranges 是对 STL 算法的革命性升级,管道语法 + 惰性求值,让数据处理代码既优雅又高效。

核心概念

cpp
#include <ranges>
#include <algorithm>

// Range:任何有 begin()/end() 的东西
// View:轻量级 Range,惰性求值,O(1) 拷贝
// Algorithm:接受 Range 的算法
// Adaptor:将 Range 变换为新 View 的工具

std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

// 传统写法
std::vector<int> result;
for (int x : v) {
    if (x % 2 == 0) result.push_back(x * x);
}

// Ranges 写法(惰性,不创建中间容器)
auto view = v
    | std::views::filter([](int x) { return x % 2 == 0; })
    | std::views::transform([](int x) { return x * x; });

for (int x : view) std::cout << x << " ";  // 4 16 36 64 100

常用 Views

cpp
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

// filter:过滤
auto evens = v | std::views::filter([](int x){ return x % 2 == 0; });

// transform:变换
auto doubled = v | std::views::transform([](int x){ return x * 2; });

// take / drop:截取
auto first3 = v | std::views::take(3);      // {1, 2, 3}
auto skip3  = v | std::views::drop(3);      // {4, 5, 6, 7, 8, 9, 10}

// take_while / drop_while
auto prefix = v | std::views::take_while([](int x){ return x < 5; });

// reverse:反转
auto rev = v | std::views::reverse;

// keys / values(用于 map)
std::map<std::string, int> m = {{"a", 1}, {"b", 2}};
for (auto& k : m | std::views::keys)   std::cout << k << " ";
for (auto& v : m | std::views::values) std::cout << v << " ";

// elements<N>(用于 tuple/pair)
std::vector<std::pair<int,int>> pairs = {{1,2},{3,4}};
for (auto x : pairs | std::views::elements<0>) std::cout << x << " ";  // 1 3

生成 Views

cpp
// iota:整数序列
for (int i : std::views::iota(1, 11)) std::cout << i << " ";  // 1..10
for (int i : std::views::iota(0) | std::views::take(5)) std::cout << i << " ";  // 0..4

// single:单元素
auto one = std::views::single(42);

// empty:空范围
auto none = std::views::empty<int>;

// istream:从流读取
#include <sstream>
std::istringstream iss("1 2 3 4 5");
for (int x : std::ranges::istream_view<int>(iss)) std::cout << x << " ";

C++23 新增 Views

cpp
// zip:打包多个范围
std::vector<int> a = {1, 2, 3};
std::vector<std::string> b = {"one", "two", "three"};
for (auto [x, s] : std::views::zip(a, b)) {
    std::cout << x << "=" << s << " ";
}

// enumerate:带索引
for (auto [i, x] : std::views::enumerate(a)) {
    std::cout << i << ":" << x << " ";  // 0:1 1:2 2:3
}

// chunk:分块
for (auto chunk : a | std::views::chunk(2)) {
    for (int x : chunk) std::cout << x << " ";
    std::cout << "| ";
}

// slide:滑动窗口
for (auto window : a | std::views::slide(2)) {
    for (int x : window) std::cout << x << " ";
    std::cout << "| ";
}

// join_with:展平并插入分隔符
std::vector<std::string> words = {"hello", "world"};
auto joined = words | std::views::join_with(' ');

Ranges 算法

cpp
std::vector<int> v = {5, 3, 1, 4, 2};

// 不需要 begin/end
std::ranges::sort(v);
std::ranges::reverse(v);
auto it = std::ranges::find(v, 3);
auto [min, max] = std::ranges::minmax(v);

// 投影(projection):指定比较键
struct Person { std::string name; int age; };
std::vector<Person> people = {{"Alice", 30}, {"Bob", 25}, {"Charlie", 35}};

std::ranges::sort(people, {}, &Person::age);  // 按 age 排序
auto youngest = std::ranges::min_element(people, {}, &Person::age);
std::ranges::sort(people, std::greater{}, &Person::name);  // 按 name 降序

// 管道 + 算法
std::vector<int> nums = {1,2,3,4,5,6,7,8,9,10};
auto sum = std::ranges::fold_left(
    nums | std::views::filter([](int x){ return x % 2 == 0; }),
    0, std::plus{}
);  // 2+4+6+8+10 = 30

收集结果(C++23 ranges::to)

cpp
// C++23:将 view 收集为容器
auto result = v
    | std::views::filter([](int x){ return x > 3; })
    | std::ranges::to<std::vector>();  // 收集为 vector

auto set_result = v
    | std::views::transform([](int x){ return x * 2; })
    | std::ranges::to<std::set>();

// C++20 的替代方案
std::vector<int> result2;
auto view = v | std::views::filter([](int x){ return x > 3; });
std::ranges::copy(view, std::back_inserter(result2));

关键认知

Ranges 的核心优势是惰性求值——管道中的 view 不会立即计算,只在遍历时按需求值,不创建中间容器。这让复杂的数据处理既高效又可读。C++23 的 ranges::to 让收集结果变得简单。

系统学习 C++ 生态,深入底层架构