C++ Ranges 是 C++20 标准引入的新特性,它重构了序列操作的范式,通过声明式编程、可组合操作和惰性求值等机制,极大提升了代码的简洁性和可维护性。但是功能还不是很完善,将就够用,在C++23又进一步完善了其中的功能。在其他语言,例如
scala、
c#、
java等功能已经很完善,如果在其他语言有类似的功底,学习起来就事半功倍
| 组件类型 | C++20 Ranges | 描述 |
|---|---|---|
| Range | std::ranges::range | 可迭代的序列 |
| View | std::ranges::view | 惰性求值的 Range |
| Algorithm | std::ranges::algorithm | 范围算法 |
| Adaptor | std::views::adaptor | 范围适配器 |
Range(范围)
Range 是任何提供迭代器对
[begin, end)的对象,包括标准容器(如vector、list、array等)、原生数组以及特殊范围(如istream_view、iota_view)。其关键特征是统一了迭代接口,消除了不同容器之间的差异
| 类型 | 迭代器能力 | 遍历次数 | 访问方式 | 典型容器 |
|---|---|---|---|---|
| input_range | 单次向前移动 | 仅一次 | 顺序读取 | istream_view、网络数据流 |
| forward_range | 多次向前移动 | 多次 | 顺序读取 | forward_list、unordered_set |
| bidirectional_range | 向前和向后移动 | 多次 | 双向遍历 | list、set、map |
| random_access_range | 任意位置跳转 | 多次 | 随机访问 | vector、deque、array |
| contiguous_range | 直接内存访问 | 多次 | 连续内存访问 | array、vector、原生数组 |
c++20限制
| 限制 | 说明 | 解决方案 |
|---|---|---|
| 无内置并行 | 需要手动实现并行 | 手动操作 |
| C++23 才有 to() | 无法直接转换为容器 | 手动构造或使用范围算法 |
| 复杂度保证 | 某些视图可能有性能陷阱 | 了解每个适配器的复杂度 |
| 编译器支持 | 需要较新的编译器 | GCC 10+, Clang 13+, MSVC 19.29+ |
view(视图)
View 是轻量级的范围包装器,具有零拷贝特性
- 惰性求值:操作延迟到实际迭代时才执行
- 时间复杂度 O(1) 的构造和析构
- 可组合性:通过
|管道符连接多个操作 - 不拥有数据:仅引用底层范围
常用视图适配器
| 适配器 | 描述 | Java Stream 等价 |
|---|---|---|
std::views::filter | 过滤元素 | filter() |
std::views::transform | 转换元素 | map() |
std::views::transform 结合 std::views::join | 转换元素 | flatMap() |
std::views::take | 取前N个元素 | limit() |
std::views::drop | 跳过前N个元素 | skip() |
std::views::reverse | 反转序列 | collect(Collectors.toList()) 与 Collections.reverse() |
std::views::keys | 提取 map 的键 | map(Map.Entry::getKey) |
std::views::values | 提取 map 的值 | map(Map.Entry::getValue) |
std::views::enumerate (c++23) | 添加索引 | 需要自定义或使用 IntStream.range() |
使用举例
这里参考
java来对比说明一下
| 特性 | C++20 Ranges | Java Stream | 说明 |
|---|---|---|---|
| 求值策略 | 惰性求值 | 惰性求值 | 两者都延迟计算 |
| 内存开销 | 零拷贝,视图引用原数据 | 中间操作不创建新集合 | C++ 更高效 |
| 类型安全 | 编译时类型检查 | 运行时类型检查 | C++ 更安全 |
| 性能 | 接近手写循环 | 有对象创建开销 | C++ 性能更好 |
| 并行支持 | 需要手动实现 | 内置 parallelStream() | Java 更简单 |
| 基本类型优化 | 无特殊处理 | 有 IntStream/LongStream | Java 避免装箱 |
| 错误处理 | 编译时错误 | 运行时异常 | C++ 更早发现问题 |
| 语法简洁性 | 管道操作符 | | 链式方法调用 | 各有优势 |
| 标准库集成 | 深度集成 STL | 独立的 Stream API | C++ 更统一 |
1、示例1
filter、transform、drop、take的使用
c++
int main() {
// 链式操作 - 惰性求值
auto result = std::views::iota(1, 21)
| std::views::filter([](int it) { return it % 3 == 0; }) // 过滤
| std::views::transform([](int it) { return it * 2 + 1; }) // 转换
| std::views::drop(2) // 跳过
| std::views::take(3) // 返回
| std::views::reverse; // 反转
// 此时还没有实际计算
for (int x : result) {
std::cout << x << ", ";
}
return 0;
}
java
IntStream.rangeClosed(1, 20)
.boxed()
.filter(it -> it % 3 == 0) // 过滤
.map(it -> it * 2 + 1) // 转换
.skip(2) // 跳过
.limit(3) // 返回
.sorted(Comparator.reverseOrder()) // 倒序
.forEach(it -> System.out.print(it + ", ")); // 打印
2、示例2
transform + join实现java的flatMap效果
c++
int main() {
std::vector<std::vector<int>> nestedVec = {
{1, 2, 3, 4, 5},
{6, 7, 8, 9,10},
{11,12,13,14,15}
};
// 类似 flatMap 的效果
auto result = nestedVec
| std::views::transform([](auto& vec) {return std::views::all(vec); })
| std::views::join // 扁平化
| std::views::filter([](int it) { return it % 3 == 0; }) // 过滤
| std::views::transform([](int it) { return it * 2 + 1; }) // 转换
| std::views::drop(2) // 跳过
| std::views::take(3); // 返回
for (auto it : result) {
std::cout << it << ", ";
}
return 0;
}
java
List<List<Integer>> list = Arrays.asList(
Arrays.asList(1, 2, 3, 4, 5),
Arrays.asList(6, 7, 8, 9, 10),
Arrays.asList(11, 12, 13, 14, 15)
);
list.stream()
.flatMap(Collection::stream) // 扁平化
.filter(it -> it % 3 == 0) // 过滤
.map(it -> it * 2 + 1) // 转换
.skip(2) // 跳过
.limit(3) // 返回
.forEach(it -> System.out.print(it + ", "));
3、示例3
keys、values的使用
c++
int main() {
std::map<string, int> map;
for (int i = 0; i < 10; i++)
{
map.emplace(std::pair("test_" + std::to_string(i), i + 10));
}
// keys
auto keys = map | std::views::keys;
for (auto it : keys) {
std::cout << it << ", ";
}
std::cout << endl;
// values
auto values = map | std::views::values;
for (auto it : values) {
std::cout << it << ", ";
}
return 0;
}
java
Map<String, Integer> map = new HashMap<String, Integer>();
for (int i = 0; i < 10; i++) {
map.put("test_" + i, i + 10);
}
// 这里返回的是set,与原来的顺序不一致了
map.keySet().forEach(it -> System.out.print(it + ", "));
System.out.println();
map.values().forEach(it -> System.out.print(it + ", "));
终止操作
| 终止操作 | 描述 | C++20 等价 |
|---|---|---|
collect() | 收集结果到容器 | 范围算法如 std::ranges::to (C++23) |
forEach() | 遍历执行 | 基于范围的 for 循环 |
reduce() | 归约操作 | std::ranges::fold (C++23) 或手动实现 |
count() | 计算元素数量 | std::ranges::distance |
findFirst() | 查找第一个元素 | std::ranges::find + 迭代器 |
anyMatch()/allMatch() | 条件检查 | std::ranges::any_of/all_of |
1、reduce
c++
int main() {
auto result = std::views::iota(1, 21)
| std::views::filter([](int it) { return it % 3 == 0; }) // 过滤
| std::views::transform([](int it) { return it * 2 + 1; }); // 转换
// 自己实现reduce
int sum = 0;
for (auto it : result)
{
sum += it;
}
std::cout << "result-> " << sum << endl;
// 借助std::accumulate实现reduce (#include <numeric>)
sum = std::accumulate(result.begin(), result.end(), 0);
std::cout << "result-> " << sum << endl;
return 0;
}
java
int sum = IntStream.rangeClosed(1, 20)
.boxed()
.filter(it -> it % 3 == 0) // 过滤
.map(it -> it * 2 + 1) // 转换
.reduce(Integer::sum)
.orElse(0);
System.out.println("result->" + sum);
2、count
c++
int main() {
auto result = std::views::iota(1, 21)
| std::views::filter([](int it) { return it % 3 == 0; }); // 过滤
// 计数
int cout = std::ranges::distance(result.begin(), result.end());
std::cout << "result-> " << cout << endl;
return 0;
}
java
long count = IntStream.rangeClosed(1, 20)
.boxed()
.filter(it -> it % 3 == 0) // 过滤
.count();
System.out.println("result->" + count);
3、findFirst
c++
int main() {
auto result = std::views::iota(1, 21)
| std::views::filter([](int it) { return it % 3 == 0; }); // 过滤
if (!result.empty())
{
std::cout << "result-> " << *(result.begin()) << endl;
}
return 0;
}
java
int result = IntStream.rangeClosed(1, 20)
.boxed()
.filter(it -> it % 3 == 0) // 过滤
.findFirst()
.orElse(0);
System.out.println("result->" + result);
4、allMatch与anyMatch
c++
int main() {
auto result = std::ranges::all_of(std::views::iota(1, 21), [](int a)
{
return a % 3 == 0 && a % 2 == 0;
}
);
std::cout << "all_of-> " << boolalpha << result << endl;
result = std::ranges::any_of(std::views::iota(1, 21), [](int a)
{
return a % 3 == 0 && a % 2 == 0;
}
);
std::cout << "any_of-> " << boolalpha << result << endl;
return 0;
}
java
boolean result = IntStream.rangeClosed(1, 20)
.boxed()
.filter(it -> it % 3 == 0) // 过滤
.allMatch(it -> it % 2 == 0);
System.out.println("allMatch-> " + result);
result = IntStream.rangeClosed(1, 20)
.boxed()
.filter(it -> it % 3 == 0) // 过滤
.anyMatch(it -> it % 2 == 0);
System.out.println("anyMatch-> " + result);
1309

被折叠的 条评论
为什么被折叠?



