C++ 20新特性之Ranges

2024-06-10 11:12
文章标签 c++ 特性 20 ranges

本文主要是介绍C++ 20新特性之Ranges,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

💡 如果想阅读最新的文章,或者有技术问题需要交流和沟通,可搜索并关注微信公众号“希望睿智”。

为什么要引入Ranges

        在C++ 20之前,我们处理容器和迭代器时,通常需要使用一系列的算法和复杂的迭代器操作。这不仅繁琐,而且很容易出错,尤其是对于复杂的算法组合。C++ 20引入Ranges的原因主要是为了提高标准库的表达力、效率和代码的可读性,以及简化序列操作的复杂度。

        任何可以被迭代的对象都可以视为Ranges,包括标准库容器(比如:vector、list、map等)、数组,甚至是由函数生成的连续或非连续元素序列。Ranges提供了一组函数和操作符,让我们能够以更加函数式的方式处理容器中的数据。这些函数和操作符通常与现有的STL算法具有相同的功能,但它们更简洁,也更易于使用。

Ranges视图

        View是Ranges的核心,相当于一个透镜,通过它可以观察Ranges的不同视图。视图是一个对数据的非拥有、可能懒加载的引用,它不会复制或修改原始数据,而是提供了一种方式来查看或处理这些数据。这使得我们可以在不修改原始数据的情况下,对数据进行过滤、映射、切片等各种复杂的操作。

        在下面的示例代码中,我们使用views::filter来过滤出一个整型容器中的偶数,得到了视图viewEven。最后,我们使用ranges::for_each来遍历视图viewEven进行输出。

#include <iostream>
#include <vector>
#include <ranges>
#include <algorithm>
using namespace std;int main()
{vector<int> vctNum = {1, 2, 3, 4, 5};auto viewEven = vctNum | views::filter([](int n) { return n % 2 == 0; });// 输出: 2 4ranges::for_each(viewEven, [](int n) { cout << n << ' '; });return 0;
}

Ranges适配器

        Ranges适配器负责接收一个输入Range(或多个输入Ranges),并根据适配器定义的规则转换或处理这些输入,最终产生一个新的Range。这个过程通常是懒惰执行的,意味着数据仅在实际访问时才会被处理。常见的适配器有如下几种。

        转换适配器(Transform Adapter):比如ranges::view::transform,它接受一个函数或Lambda,对输入Range中的每个元素应用该函数,并生成一个新元素。

        过滤适配器(Filter Adapter):比如ranges::view::filter,它根据提供的谓词筛选出符合条件的元素,丢弃不符合条件的元素。

        取前N个元素适配器(Take Adapter):比如ranges::view::take,仅取Range的前N个元素,这对于处理有限制的数据流非常有用。

        丢弃前N个元素适配器(Drop Adapter):比如ranges::view::drop,丢弃Range的前N个元素,然后返回剩余的元素。

        合并适配器(Concatenate Adapter):比如ranges::view::join,用于将一系列Range(通常为Range的Range)连接成一个单一的Range。

        切片适配器(Slicing Adapter):比如ranges::view::slice,可以取出Range的一个子区间,类似于数组的切片操作。

        在下面的示例代码中,我们使用转换适配器将原始的vctNum转换成了一个视图,这个视图中每个元素的值为vctNum中每个元素的平方。最后,我们使用ranges::for_each来遍历视图viewSquared进行输出。注意:views::transform并不会修改原始的vctNum,而是产生了一个新的Range视图。

#include <iostream>
#include <vector>
#include <ranges>
#include <algorithm>
using namespace std;int main()
{vector<int> vctNum = {1, 2, 3, 4, 5};auto viewSquared = vctNum | views::transform([](int n) { return n * n; });// 输出: 1 4 9 16 25ranges::for_each(viewSquared, [](int n) { cout << n << ' '; });return 0;
}

Ranges算法

        Ranges还提供了一组与STL算法类似的算法,这些算法可以直接在Range上操作,而无需显式地使用繁琐的迭代器。在下面的示例代码中,我们使用std::ranges::find来在vctNum中查找一个元素。

#include <iostream>
#include <vector>
#include <ranges>
#include <format>
using namespace std;int main()
{vector<int> vctNum = {1, 2, 3, 4, 5};auto it = ranges::find(vctNum, 2);if (it != vctNum.end()){cout << format("found {}", *it) << endl;}else{cout << "not found" << endl;}return 0;
}

管道操作符

        管道操作符,也就是“|”符号,是C++ 20中一个重载过的操作符。在Ranges库中,它用于连接一系列的操作,形成一个数据处理的管道。这意味着,数据可以从一个操作流向下一个操作,每个环节只关注处理数据,而不需要关心数据的来源和去向。当我们将numbers | ranges::view::...这样的表达式放在一起时,意味着我们正在创建一个处理numbers集合的管道,其中...代表具体的View操作。

        在下面的示例代码中,我们首先过滤出偶数,然后计算它们的平方,这展示了ranges的简洁性和表达力。通过这样的管道表达式,C++程序员能够以一种声明性的方式写出高效且易于理解的代码,特别适合进行数据处理和分析任务。

#include <iostream>
#include <vector>
#include <ranges>
#include <algorithm>
using namespace std;int main()
{vector<int> vctNum = {1, 2, 3, 4, 5, 6};auto viewResult = vctNum | views::filter([](int x) { return x % 2 == 0; }) | views::transform([](int x) { return x * x; });// 输出:4 16 36ranges::for_each(viewResult, [](int x) { cout << x << " "; });return 0;
}

这篇关于C++ 20新特性之Ranges的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1048001

相关文章

利用c++判断水仙花数并输出示例代码

《利用c++判断水仙花数并输出示例代码》水仙花数是指一个三位数,其各位数字的立方和恰好等于该数本身,:本文主要介绍利用c++判断水仙花数并输出的相关资料,文中通过代码介绍的非常详细,需要的朋友可以... 以下是使用C++实现的相同逻辑代码:#include <IOStream>#include <vec

基于C++的UDP网络通信系统设计与实现详解

《基于C++的UDP网络通信系统设计与实现详解》在网络编程领域,UDP作为一种无连接的传输层协议,以其高效、低延迟的特性在实时性要求高的应用场景中占据重要地位,下面我们就来看看如何从零开始构建一个完整... 目录前言一、UDP服务器UdpServer.hpp1.1 基本框架设计1.2 初始化函数Init详解

C++ 右值引用(rvalue references)与移动语义(move semantics)深度解析

《C++右值引用(rvaluereferences)与移动语义(movesemantics)深度解析》文章主要介绍了C++右值引用和移动语义的设计动机、基本概念、实现方式以及在实际编程中的应用,... 目录一、右值引用(rvalue references)与移动语义(move semantics)设计动机1

C++ move 的作用详解及陷阱最佳实践

《C++move的作用详解及陷阱最佳实践》文章详细介绍了C++中的`std::move`函数的作用,包括为什么需要它、它的本质、典型使用场景、以及一些常见陷阱和最佳实践,感兴趣的朋友跟随小编一起看... 目录C++ move 的作用详解一、一句话总结二、为什么需要 move?C++98/03 的痛点⚡C++

详解C++ 存储二进制数据容器的几种方法

《详解C++存储二进制数据容器的几种方法》本文主要介绍了详解C++存储二进制数据容器,包括std::vector、std::array、std::string、std::bitset和std::ve... 目录1.std::vector<uint8_t>(最常用)特点:适用场景:示例:2.std::arra

C++构造函数中explicit详解

《C++构造函数中explicit详解》explicit关键字用于修饰单参数构造函数或可以看作单参数的构造函数,阻止编译器进行隐式类型转换或拷贝初始化,本文就来介绍explicit的使用,感兴趣的可以... 目录1. 什么是explicit2. 隐式转换的问题3.explicit的使用示例基本用法多参数构造

C++,C#,Rust,Go,Java,Python,JavaScript的性能对比全面讲解

《C++,C#,Rust,Go,Java,Python,JavaScript的性能对比全面讲解》:本文主要介绍C++,C#,Rust,Go,Java,Python,JavaScript性能对比全面... 目录编程语言性能对比、核心优势与最佳使用场景性能对比表格C++C#RustGoJavapythonjav

C++打印 vector的几种方法小结

《C++打印vector的几种方法小结》本文介绍了C++中遍历vector的几种方法,包括使用迭代器、auto关键字、typedef、计数器以及C++11引入的范围基础循环,具有一定的参考价值,感兴... 目录1. 使用迭代器2. 使用 auto (C++11) / typedef / type alias

C++ scoped_ptr 和 unique_ptr对比分析

《C++scoped_ptr和unique_ptr对比分析》本文介绍了C++中的`scoped_ptr`和`unique_ptr`,详细比较了它们的特性、使用场景以及现代C++推荐的使用`uni... 目录1. scoped_ptr基本特性主要特点2. unique_ptr基本用法3. 主要区别对比4. u

C++11中的包装器实战案例

《C++11中的包装器实战案例》本文给大家介绍C++11中的包装器实战案例,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录引言1.std::function1.1.什么是std::function1.2.核心用法1.2.1.包装普通函数1.2.