C++范围操作(1)


C++ Ranges 是 C++20 标准引入的新特性,它重构了序列操作的范式,通过声明式编程、可组合操作和惰性求值等机制,极大提升了代码的简洁性和可维护性。但是功能还不是很完善,将就够用,在C++23又进一步完善了其中的功能。在其他语言,例如 scalac#java等功能已经很完善,如果在其他语言有类似的功底,学习起来就事半功倍

组件类型C++20 Ranges描述
Rangestd::ranges::range可迭代的序列
Viewstd::ranges::view惰性求值的 Range
Algorithmstd::ranges::algorithm范围算法
Adaptorstd::views::adaptor范围适配器

Range(范围)

Range 是任何提供迭代器对 [begin, end) 的对象,包括标准容器(如 vectorlistarray 等)、原生数组以及特殊范围(如 istream_viewiota_view)。其关键特征是统一了迭代接口,消除了不同容器之间的差异

类型迭代器能力遍历次数访问方式典型容器
input_range单次向前移动仅一次顺序读取istream_view、网络数据流
forward_range多次向前移动多次顺序读取forward_listunordered_set
bidirectional_range向前和向后移动多次双向遍历listsetmap
random_access_range任意位置跳转多次随机访问vectordequearray
contiguous_range直接内存访问多次连续内存访问arrayvector、原生数组

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 RangesJava Stream说明
求值策略惰性求值惰性求值两者都延迟计算
内存开销零拷贝,视图引用原数据中间操作不创建新集合C++ 更高效
类型安全编译时类型检查运行时类型检查C++ 更安全
性能接近手写循环有对象创建开销C++ 性能更好
并行支持需要手动实现内置 parallelStream()Java 更简单
基本类型优化无特殊处理有 IntStream/LongStreamJava 避免装箱
错误处理编译时错误运行时异常C++ 更早发现问题
语法简洁性管道操作符 |链式方法调用各有优势
标准库集成深度集成 STL独立的 Stream APIC++ 更统一

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);
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值