STL库的ranges

STL库的ranges

在C++ STL标准库的<ranges>(C++20中引入)中,定义了一套全面的关于范围的概念、类、模板、函数以及其他相关组件,旨在提高对元素序列的抽象化处理能力。主要包括以下几个方面:

  1. 范围(Range):定义了一系列标准要求,规定了怎样的对象可被视为一个范围。
  2. 视图(Views):提供了一系列轻量级、不可变且延迟计算的类模板及相关工具函数。
  3. 范围友好算法(Range-Algorithms):对传统STL库的算法进行了升级和扩展,使其可以直接作用于范围对象,并能充分利用视图的特性,提高了算法的灵活性和效率。
  4. 辅助工具:一些工具函数,使得对范围的操作更为便捷。

范围(ranges)

范围(Range)是一个可以被迭代(遍历)的对象,它可以是标准容器(如vector、list)、原生数组、字符串或者任何能够提供迭代器接口的数据结构。在Ranges中,一个范围不仅仅包括元素序列,还定义了如何访问这些元素的规则。它指向一系列元素,从概念上看类似于一对begin与end迭代器。

范围概念

范围由概念(concept)定义。概念(concept)是C++语言的一个核心特性,它正式引入于C++20标准。与类定义不同,概念定义描述了一组行为特征,而不用关心具体的类型。这意味着只要一个对象具有期望的行为(如特定的成员变量或方法,或者让作为参数能让某段代码编译通过),就可以在代码中当作符合某种约定的对象来处理,而无需关心对象的确切类型。

引入范围概念,让处理序列数据的代码与承载序列数据的具体容器实现无关,两者解耦。这也是“鸭子类型”的编程范式。

concept代表的编程范式也被称为鸭子类型(Duck Typing),这个概念源自一句俗语:“如果它走路像鸭子、叫声像鸭子,那么它就是鸭子。” 动态类型语言中广泛使用鸭子类型编程范式,例如Ruby、Python。而在静态类型语言C++中,通过引入Concept机制,也巧妙借鉴了这一灵活处理对象特性的方法论。

STL的<ranges>定义了std::ranges::range概念(concept),凡是符合range概念的对象都可以被当作范围对象。从代码定义上看,凡是能从中得到begin和end两个迭代器的对象都可以看作范围。如下代码所示:

std::ranges::range代码定义

template< class T >
concept range = requires( T& t ) {ranges::begin(t); // equality-preserving for forward iteratorsranges::end  (t);
};

范围是个大范畴,除了std::ranges::range定义了基本的范围概念外,<ranges>命名空间里还定义了特殊的范围概念,形成一个类似于类继承的树形关系结构。根据支持迭代器的不同,一部分范围概念定义可以组成这样的树形结构。

range
|- view
|- common_range
|- sized_range
|- output_range
|- input_range|- forward_range|- bidirectional_range|- random_access_range|- contiguous_range

sized_range为例,这个概念定义 “符合range概念并可以作为参数执行size(x)” 的东西均属于此概念。

    _EXPORT_STD template <class _Rng>concept sized_range = range<_Rng> && requires(_Rng& __r) { _RANGES size(__r); };

借用范围 borrowed range

借用范围是一种比较重要的概念,是指不会因为迭代而被消耗的数据序列,例如引用原始数据的视图或者引用类型的容器。借用范围生命周期结束后,其迭代器仍然有效。

概念borrowed_range判断一个类型是否为借用范围(borrowed range),定义如下:

template<class _Range>
concept borrowed_range = range<_Range> &&
(is_lvalue_reference_v<_Range> || enable_borrowed_range<remove_cvref_t<_Range>>);// 由模板特化指定为true。
template <class>
inline constexpr bool enable_borrowed_range = false;

如果要创建一个符合borrowed_range概念的类,首先这个类型要满足range概念的要求,其次需要特化enable_borrowed_range以满足enable_borrowed_range的要求。

视图(views)

视图(views)是一种轻量级的范围,它不会存储元素,而是对其他范围进行变换、过滤等操作的结果。视图是惰性求值的,这意味着操作直到真正需要结果时才会执行,这有助于提升效率。视图能够对范围对象进行各种转换、筛选和聚合等运算,有以下特点:

  1. 不拥有数据
    与数据的拥有权解耦,即视图处理数据但不拥有数据,因此不必管资源的申请和释放,同时也不会修改底层数据。

  2. 不复制数据

    通常不需要在内存中分配新的数据结构,而是通过引用或转换现有的数据进行操作,这意味着运算中通常产生较少的的额外开销。

  3. 惰性计算(Lazy Evaluation)
    惰性计算就是“按需计算”,即它们只在需要时才会执行。这意味着,当你创建一个视图时,不会立即对底层数据进行任何计算,只有在实际使用时才会触发计算。这种延迟计算的特性可以节省内存和计算资源,尤其是在处理大型数据集的场景中。

  4. 函数式编程与链式风格
    视图本身符合范围概念的要求,因此一个视图可以作为另一个视图的输入,且基于视图的函数性、不可变性和无副作用性,这样就支持了函数式编程范式。辅以管道运算符“|”,可以实现漂亮的链式编程风格。

常用视图

关于视图相关的内容定义在std::ranges::views名字空间中,包含但不限于以下内容:

  1. filter:创建一个仅包含符合特定条件的元素的视图。
  2. transform:对每个元素应用给定的转换函数,并生成一个新的视图。
  3. take:创建一个包含指定数量元素的视图。
  4. drop:创建一个去除指定数量元素后的视图。
  5. split:将范围分割成指定大小的子范围序列。
  6. reverse:反转范围中的元素顺序。
  7. join:将范围的范围中的子范围连接成单个范围。
  8. elements:从范围中的元组中选择指定索引的元素,并将其表示为一个范围。

std::views是对std::ranges::views的简写,它是通过一个命名空间别名实现的,旨在简化代码的书写。

namespace views = ranges::views;

管道运算符

视图的使用最好配合管道运算符。管道运算符就是位或运算符|的重定义,用法就像Unix的管道命令,可以将视图像管道一样连接起来,实现类似于Java Stream API的流式数据处理管道。

下面是一个简单的例子:

#include <iostream>
#include <ranges>
#include <vector>int main() {// 创建一个整数向量std::vector<int> numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };// 使用std::views::filter过滤出所有偶数,将其值增加一倍,再取前三个值。auto result_view = numbers| std::views::filter([](int i) { return i % 2 == 0; })| std::views::transform([](int i) { return i * 2; })| std::views::take(3);for (int number : result_view) {std::cout << number << ' ';}return 0;
}

注意std::views::take(3)意味着只取前三个结果,因为视图是惰性计算的,因此程序并不会处理numbers的所有元素。并且整个处理过程看不到中间变量,也没有构建用于存放中间结果的容器。此处可以看到视图的非常明显的价值:

  • 节约内存和计算资源
  • 代码风格清晰紧凑,可读性强
  • 几乎没有副作用
  • 产生bug的几率少

范围适配器

视图也被称为“范围适配器”(Range adapters),就像电源适配器(例如交流转直流,220V转110V)一样,它们是作用在范围之上的对其数据做转换的“适配器”,通过提供动态的、可组合的数据处理管道,使得C++程序员能够以声明式、函数式的方式处理数据,而无需关注底层实现细节,从而极大地增强了代码的表达力和效率。

范围友好算法

引入ranges之后,STL中的很多算法(原先algorithm模块中的函数)被改写并放到了std::ranges命名空间中(为了兼容性跟原先的std命名空间中的算法函数并存),这些新的算法接受一个range作为参数,而不是原来的begin和end两个参数,例如std::ranges::sort(range)。

以下是常用的一些范围友好算法:

  • 条件判断
    • ranges::all_of
    • ranges::any_of
    • ranges::none_of
  • 遍历处理
    • ranges::for_each & ranges::for_each_n
  • 转换
    • ranges::transform
    • ranges::reverse
  • 生成
    • ranges::fill
    • ranges::generate
    • ranges::iota
  • 查找比较
    • ranges::count & ranges::count_if
    • ranges::find & ranges::find_if
    • ranges::starts_with & ranges::ends_with
    • ranges::contains
    • ranges::search
  • 复制搬运
    • ranges::copy & ranges::copy_if
    • ranges::move
  • 排序和半排序
    • ranges::is_sorted
    • ranges::sort & ranges::stable_sort
    • ranges::partial_sort & ranges::nth_element
  • 分区
    • ranges::is_partition & ranges::partition
  • 二分查找
    • ranges::lower_bound & ranges::upper_bound
    • ranges::binary_search
  • 集合运算 - 提供集合数据结构相关的操作
    • ranges::merge
    • ranges::include
    • ranges::set_difference & ranges::set_intersection & ranges::set_union 集合的求差集、交集、并集运算
  • 堆运算 - 提供堆数据结构相关的操作
    • ranges::is_heap & ranges::make_heap
    • ranges::push_heap & ranges::pop_heap & ranges::sort_heap 入队、出堆与堆排序

心得体会

C++引入了概念concept,提供了更高层级的抽象,<ranges>是基于概念的,把抽象推广至广泛的场景。基于range的算法有更好的通用型。程序员使用自己专门设计的容器(可能是为了自己特定应用场景而做了特别的优化)与ranges互动,复用ranges提供的内容,减少自己的工作量。

std::ranges::views和管道运算符提供了一种现代化的、更加直观的方式来处理序列操作,使代码更简洁易读。

总之,使用<ranges>可以让代码更加现代化、简洁和高效,提高开发效率并减少错误的可能性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/846089.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

lsof 命令

lsof&#xff08;list open files&#xff09;是一个列出当前系统打开文件的工具。在linux环境下&#xff0c;任何事物都以文件的形式存在&#xff0c;通过文件不仅仅可以访问常规数据&#xff0c;还可以访问网络连接和硬件。所以如传输控制协议 (TCP) 和用户数据报协议 (UDP) …

什么是TLAB?

这个得从内存申请说起。 一般而言生成对象需要向堆中的新生代申请内存空间&#xff0c;而堆又是全局共享的&#xff0c;像新生代内存又是规整的&#xff0c;是通过一个指针来划分的。 内存是紧凑的&#xff0c;新对象创建指针就右移对象大小size即可&#xff0c;这叫指针加法…

怎么通过互联网远程控制电脑?

远程访问又称为网络远程控制&#xff0c;它使用户能够通过互联网连接两台设备以解决问题。进行控制的电脑称为控制端&#xff0c;被控制的电脑则称为被控端。在远程访问过程中&#xff0c;控制端电脑掌握整个连接的操作。远程控制软件会捕获被控端电脑的操作&#xff0c;并在主…

绘画智能体分享

这是您请求的故宫雪景图&#xff0c;角落有一只可爱的胖猫&#xff0c;采用了水墨画风格&#xff0c;类似于张大千的作品。希望您喜欢这幅画&#xff01; &#x1f3a8; 选项 1【转变风格】——将这幅画转变为梵高的后印象派风格&#xff0c;增添一些梵高特有的笔触和色彩。 &…

Superset二次开发之Github项目推送到GitLab仓库

以下是从GitHub克隆Superset项目并将其推送到GitLab的详细操作步骤 lab 地址: xxx lab 配置: 生成SSH密钥 ssh-keygen -t rsa -b 4096 -C "邮箱地址" 默认情况下密钥会生成在~/.ssh (/c/Users/Administrator/.ssh/id_rsa)目录下。 公钥添加到GitLab: 打开公钥文件…

# 第十八届东北ccpc程序设计竞赛(A)

ProblemA paper watering 题意: 对一个数进行k次操作&#xff0c;操作有两种类型:1.将数开根号后向下取整&#xff1b;2.将该数取平方。求出这个数字能改成多少个不同的数&#xff08;包括一开始的原数&#xff09;. 思路&#xff1a; 如果这个数开方后为原来数字&#xff…

enum编程入门:探索枚举类型的奥秘

enum编程入门&#xff1a;探索枚举类型的奥秘 在编程的世界里&#xff0c;enum&#xff08;枚举&#xff09;类型是一种特殊的数据类型&#xff0c;它允许我们为变量设置一组预定义的、有限的值。这种类型在很多编程语言中都得到了广泛的应用&#xff0c;为开发者提供了更加清…

短时间内如何顺利通过 Java 面试?

今天我们来探讨一个重要的话题&#xff1a;短时间内如何顺利通过 Java 面试&#xff1f; 在此之前&#xff0c;我正在精心编写一套完全面向小白的 Java 自学教程&#xff0c;我相信这套教程会非常适合正在努力提升的你。教程里面涵盖了丰富全面的编程教学内容、详细生动的视频…

在HTML中写入JavaScript

在网站中添加JavaScript代码是为了增加页面的交互性。这可以通过直接在HTML文件中写入JavaScript代码&#xff0c;或者通过引入外部JavaScript文件来实现。下面我将详细说明这两种方法。 直接在HTML中写入JavaScript 这种方法简单直接&#xff0c;适合用于快速测试或添加少量的…

liunx telnet 安装

1&#xff0c;离线包下载 &#xff08;1&#xff09;首先下载 telnet-client (或 telnet) 客户端软件包&#xff0c;这里我们下载 telnet-0.17-64.el7.x86_64.rpm 版本&#xff1a; 下载地址&#xff1a;http://www.rpmfind.net/linux/rpm2html/search.php?querytelnet 安装…

上网行为监控软件大盘点:好用的上网行为管理软件一览

这是后台私信来的客户发来的图片&#xff0c;他们老板想要给自己公司安装上网行为监控软件。 可见&#xff0c;企业对员工上网行为的监控与管理已经是很多企业都开始重视的问题。 上网行为管理软件能够帮助企业确保网络资源得到合理分配&#xff0c;提升工作效率&#xff0c;…

ch5链路层和局域网

回顾TCP/IP参考模型&#xff0c;明确链路层和物理层在整个模型中的地位&#xff0c;简要提出链路层要解决的问题是单段链路的数据传输&#xff0c;物理层解决的是数字信号与电气信号之间的相互转换。 链路层概述 节点&#xff1a;主机和路由器(包括网桥和交换机) 链路&#xf…

QT 信号和槽教程,窗体和控件对象之间的沟通一般都使用信号和槽

Qt的信号和槽&#xff08;Signals and Slots&#xff09;机制是一种强大的对象间通信方式&#xff0c;它允许对象在完全解耦的情况下相互通信。以下是关于Qt信号和槽的简明教程&#xff1a; 基本概念 信号&#xff08;Signal&#xff09;&#xff1a;信号是由Qt对象发出的通知…

(2024,LoRA,全量微调,低秩,强正则化,缓解遗忘,多样性)LoRA 学习更少,遗忘更少

LoRA Learns Less and Forgets Less 公和众和号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0. 摘要 1. 引言 2. 背景 3. 实验设置 3.2 使用编码和数学基准测试来衡量学习&#xff08;目标域…

数据分析必备:一步步教你如何用Pandas做数据分析(14)

1、Pandas 聚合 Pandas 聚合的操作实例 创建滚动&#xff0c;扩展和ewm对象后&#xff0c;可以使用多种方法对数据执行聚合。 1.1、对DataFrame聚合 我们创建一个DataFrame并对其应用聚合 import pandas as pd import numpy as np df pd.DataFrame(np.random.randn(10, 4)…

jpeg编码学习

正点原子stm32教程提到过jpeg解码库libjpeg&#xff0c;但是没有提到jpeg编码&#xff0c;我也好奇jpeg编码怎么实现&#xff0c;用代码怎么生成jpeg文件的。所以最近学习了jpeg编码&#xff0c;在这里做记录。 参考文章 jpeg图片格式详解 https://blog.csdn.net/yun_hen/art…

跨越障碍:解决复杂网页数据提取的挑战

在数字化时代&#xff0c;网页数据提取已成为获取信息的重要途径。然而&#xff0c;随着网页设计的日益复杂化&#xff0c;从这些页面中提取有用数据的任务也变得更加困难。本文将探讨在面对复杂网页时&#xff0c;如何有效地提取所需数据&#xff0c;并分析可能遇到的挑战及其…

一文了解-电子公司组织架构

以下仅为一般研发设计公司的岗位&#xff0c; 不包括工厂。 总经理 柏柏有话说: 不多说&#xff0c;人中龙凤、群山俯首、众神膜拜&#xff01; 项目总监 产品总监 软件总监 测试总监 销售总监 人事总监 财务总监 采购总监 柏柏有话说: 这么说吧&#xff0c; 柏柏的前同龄同事…

共计3万字!从零开始创建一个小规模的稳定扩散模型!

节前&#xff0c;我们星球组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、参加社招和校招面试的同学。 针对算法岗技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备、面试常考点分享等热门话题进行了深入的讨论。 合集&#x…

MYSQL数据库细节详细分析

MYSQL数据库的数据类型(一般只需要用到这些) 整型类型&#xff1a;用于存储整数值&#xff0c;可以选择不同的大小范围来适应特定的整数值。 TINYINTSMALLINTMEDIUMINTINTBIGINT 浮点型类型&#xff1a;用于存储带有小数部分的数值&#xff0c;提供了单精度&#xff08;FLOA…