在 C++20 标准发布后,std::ranges 库为我们提供了一套强大且直观的工具,能够让对容器、迭代器以及序列进行处理变得更为简洁和安全。本文将从基础概念出发,介绍如何使用 std::ranges 进行常见的数据处理任务,并通过代码示例展示其优势。
1. 何为 std::ranges?
std::ranges 是对 C++20 迭代器模型的一次重构,它将原本需要使用迭代器、函数对象、算法等多重配合的复杂逻辑,拆解成一系列轻量级、可组合的“范围(range)”与“视图(view)”。主要特点:
- 惰性求值:视图不会立即执行,只有在需要遍历时才会真正计算。
- 可组合性:通过链式调用,能够以函数式风格组合多个操作。
- 类型安全:编译期检查,减少运行时错误。
2. 基础使用:链式视图
#include <iostream>
#include <vector>
#include <ranges>
int main() {
std::vector <int> numbers{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 取偶数、平方后过滤小于 50 的结果
for (auto val : numbers
| std::views::filter([](int n){ return n % 2 == 0; })
| std::views::transform([](int n){ return n * n; })
| std::views::filter([](int n){ return n < 50; })
) {
std::cout << val << ' ';
}
std::cout << '\n';
}
输出:
4 16 36
关键点说明
std::views::filter:按条件过滤元素。std::views::transform:对每个元素执行变换。- 视图链式调用,代码易读、易维护。
3. 高级应用:自定义视图与算法
3.1 自定义视图
假设我们想对每个字符串的首字母大写化,可以创建一个自定义视图:
#include <string_view>
#include <algorithm>
namespace std::ranges {
struct uppercase_view : std::ranges::view_base {
using iterator_category = std::forward_iterator_tag;
using value_type = char;
using difference_type = std::ptrdiff_t;
struct iterator {
char* cur;
iterator(char* p) : cur(p) {}
char& operator*() const { return *cur; }
iterator& operator++() { ++cur; return *this; }
bool operator!=(const iterator& other) const { return cur != other.cur; }
};
char* begin_;
std::size_t len_;
iterator begin() const { return iterator(begin_); }
iterator end() const { return iterator(begin_ + len_); }
};
constexpr uppercase_view uppercase(std::string_view sv) {
return uppercase_view{const_cast<char*>(sv.data()), sv.size()};
}
}
使用方式:
for (char c : std::ranges::uppercase(std::string_view("hello world"))) {
if (std::islower(c)) c = std::toupper(c);
std::cout << c;
}
输出:HELLO WORLD
3.2 与标准算法组合
std::ranges 也允许将视图与标准算法无缝结合。例如,使用 std::ranges::sort:
#include <vector>
#include <ranges>
#include <algorithm>
int main() {
std::vector <int> data{9, 5, 3, 7, 1};
auto sorted = data | std::views::all; // 把容器转成范围
std::ranges::sort(sorted);
for (int n : sorted) std::cout << n << ' ';
std::cout << '\n';
}
输出:1 3 5 7 9
4. 性能与最佳实践
- 尽量使用懒惰视图:只在真正需要遍历时才会触发计算,减少不必要的拷贝。
- 避免不必要的中间容器:链式视图天然支持流式处理,不需要额外的
std::vector存储中间结果。 - 注意视图生命周期:自定义视图中持有的指针/引用一定要确保有效,避免悬空指针。
5. 小结
std::ranges 让 C++ 的数据处理更加直观、模块化。通过视图的惰性求值和强大的组合能力,能够写出既简洁又高效的代码。无论是过滤、变换还是排序,使用 std::ranges 都能让代码更加贴近函数式编程风格,同时保持 C++ 的性能优势。未来随着标准的进一步完善,std::ranges 将成为 C++ 高效数据操作的核心工具。