C++20 引入了 ranges(范围),这是一个强大且优雅的抽象,用于处理序列(如数组、vector 等)。相比传统的迭代器或旧式循环,Ranges 提高了代码的可读性、可组合性和性能。
什么是 Range?
在 C++20 中,range(范围) 是一种抽象,代表一个可以迭代的元素序列。它与 views(视图) 和 actions(操作) 如过滤、转换等配合使用非常自然。
传统循环 vs 基于 Range 的循环
#include <iostream>
#include <vector>
int main() {
std::vector<int> v = {1, 2, 3, 4};
// 旧式循环
for (auto it = v.begin(); it != v.end(); ++it)
std::cout << *it << ' ';
// 基于范围的循环(C++11)
for (auto x : v)
std::cout << x << ' ';
}
Range Views(视图)
View 是惰性的、可组合的范围操作。除非需要,一般不会复制数据。
Filter 和 Transform 示例
#include <iostream>
#include <vector>
#include <ranges>
int main() {
std::vector<int> v = {1, 2, 3, 4, 5, 6};
auto even_doubled = v
| std::views::filter([](int n) { return n % 2 == 0; })
| std::views::transform([](int n) { return n * 2; });
for (int n : even_doubled)
std::cout << n << ' '; // 输出:4 8 12
}
常见的 Views
| View | 描述 |
|---|---|
| std::views::filter | 保留符合条件的元素 |
| std::views::transform | 对每个元素应用函数 |
| std::views::take(n) | 获取前 n 个元素 |
| std::views::drop(n) | 跳过前 n 个元素 |
| std::views::reverse | 反转范围 |
| std::views::iota(a, b) | 生成从 a 到 b-1 的范围 |
使用 iota 和 reverse
#include <ranges>
#include <iostream>
int main() {
for (int i : std::views::iota(1, 6) | std::views::reverse)
std::cout << i << ' '; // 输出:5 4 3 2 1
}
组合视图操作
你可以使用管道符 | 流式地组合多个视图操作。
#include <vector>
#include <ranges>
#include <iostream>
int main() {
std::vector<int> v = {5, 10, 15, 20};
auto result = v
| std::views::transform([](int x) { return x + 1; })
| std::views::filter([](int x) { return x % 2 == 0; });
for (int x : result)
std::cout << x << ' '; // 输出:6 16
}
实用示例
1. 过滤偶数
#include <iostream>
#include <vector>
#include <ranges>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5, 6};
auto evens = numbers
| std::views::filter([](int n) { return n % 2 == 0; });
for (int n : evens)
std::cout << n << ' '; // 输出:2 4 6
}
2. 将奇数翻倍
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
auto doubled_odds = numbers
| std::views::filter([](int n) { return n % 2 != 0; })
| std::views::transform([](int n) { return n * 2; });
for (int n : doubled_odds)
std::cout << n << ' '; // 输出:2 6 10
}
3. 反转序列
int main() {
std::vector<int> nums = {10, 20, 30};
auto reversed = nums | std::views::reverse;
for (int n : reversed)
std::cout << n << ' '; // 输出:30 20 10
}
4. 生成数值序列
#include <ranges>
int main() {
for (int i : std::views::iota(1, 6))
std::cout << i << ' '; // 输出:1 2 3 4 5
}
5. 获取前 N 个元素
int main() {
auto infinite = std::views::iota(1); // 无限序列
auto first5 = infinite | std::views::take(5);
for (int i : first5)
std::cout << i << ' '; // 输出:1 2 3 4 5
}
6. 计算前 5 个奇数的平方和
#include <numeric>
int main() {
auto odd_squares = std::views::iota(1)
| std::views::filter([](int x) { return x % 2 == 1; })
| std::views::transform([](int x) { return x * x; })
| std::views::take(5);
int sum = std::accumulate(odd_squares.begin(), odd_squares.end(), 0);
std::cout << "和 = " << sum << '\n'; // 输出:和 = 165
}
7. 判断是否所有元素都为正数
#include <ranges>
#include <algorithm>
#include <vector>
#include <iostream>
int main() {
std::vector<int> nums = {1, 2, 3};
bool all_positive = std::ranges::all_of(nums, [](int n) { return n > 0; });
std::cout << std::boolalpha << all_positive << '\n'; // 输出:true
}
8. 自定义管道函数
auto pipeline = [](const std::vector<int>& v) {
return v
| std::views::filter([](int x) { return x % 2 == 0; })
| std::views::transform([](int x) { return x * 10; });
};
int main() {
std::vector<int> nums = {1, 2, 3, 4};
for (int x : pipeline(nums))
std::cout << x << ' '; // 输出:20 40
}
性能提示
- Ranges 是惰性的:仅在需要时才处理元素。
- 避免不必要的分配与复制。
- 适合处理大型数据或函数管道。
何时不适合使用 Ranges
- 在对性能极度敏感的内循环中,STL 抽象可能较慢。
- 当项目尚未迁移到 C++20。
参考资料
强烈推荐
- 英国代购-畅购英伦
- TopCashBack 返现 (英国购物必备, 积少成多, 我2年来一共得了3000多英镑)
- Quidco 返现 (也是很不错的英国返现网站, 返现率高)
- 注册就送10美元, 免费使用2个月的 DigitalOcean 云主机(性价比超高, 每月只需5美元)
- 注册就送10美元, 免费使用4个月的 Vultr 云主机(性价比超高, 每月只需2.5美元)
- 注册就送10美元, 免费使用2个月的 阿里 云主机(性价比超高, 每月只需4.5美元)
- 注册就送20美元, 免费使用4个月的 Linode 云主机(性价比超高, 每月只需5美元) (折扣码: PodCastInit2022)
- PlusNet 英国光纤(超快, 超划算! 用户名 doctorlai)
- 刷了美国运通信用卡一年得到的积分 换了 485英镑
- 注册就送50英镑 – 英国最便宜最划算的电气提供商
- 能把比特币莱特币变现的银行卡! 不需要手续费就可以把虚拟货币法币兑换
微信公众号: 小赖子的英国生活和资讯 JustYYUK