(CPP20) 简单实现span

news/2024/2/29 3:39:58

文章目录

  • 视图的概念
  • 常见连续序列测试
  • 简单实现
    • Code
    • 直接引用对象
    • 传入首位置
  • END

视图的概念

在不传递引用的情况下,传递对象在很多时候会巨大的性能损耗。

而入错传递的是一个视图,这个视图能够指向原对象,那么这个直接传递的开销也是我们可以接受的。

在C++20中,std::span是一种能够指代连续序列的数据结构。

std::span - cppreference.com

具体效果如下:

#include <iostream>
#include <span>
#include <vector>struct Node {int x = 0;Node(int x) : x(x) {printf("[%s]=>[%p]=>[%d]\n", __func__, this, x);}Node(const Node& lhs) {x = lhs.x;printf("[%s]=>[%p=>%p]=>[%d]\n", __func__, &lhs, this, x);}operator int() {return x;}
};void show_by_vec(std::vector<Node> arr) {std::cout << __func__ << std::endl;for (auto&& x : arr) {std::cout << x << ' ';}std::cout << std::endl;
}void show_by_span(std::span<Node> arr) {std::cout << __func__ << std::endl;for (auto&& x : arr) {std::cout << x << ' ';}std::cout << std::endl;
}int main() {std::vector<Node> arr = {1, 2, 3};std::cout << "++++++++++++++++++++++++++++" << std::endl;show_by_vec(arr);show_by_span(arr);
}

输出如下:

从输出可以看出,span相比较于直接传递对象不会有过多的开销

[Node]=>[000001368f2513e0]=>[1]
[Node]=>[000001368f2513e4]=>[2]
[Node]=>[000001368f2513e8]=>[3]
++++++++++++++++++++++++++++
[Node]=>[000001368f2513e0=>000001368f251400]=>[1]
[Node]=>[000001368f2513e4=>000001368f251404]=>[2]
[Node]=>[000001368f2513e8=>000001368f251408]=>[3]
show_by_vec
1 2 3
show_by_span
1 2 3

常见连续序列测试

常见的连续序列大概有以下几种

  • C式数组
  • std::vector
  • std::array

注意:

  • std::list, std::deque这些都不是连续序列
  • 特别的,变长数组虽然也是连续的,但是不能用与std::span
#include <array>
#include <iostream>
#include <list>
#include <span>
#include <vector>namespace name = std;
void show(name::span<int> arr) {for (auto&& elem : arr) {std::cout << elem << ' ';}std::cout << std::endl;
}int main() {// 传统的C式数组int c_arr[] = {1, 2, 3, 4};show(c_arr);                      // 直接传数组show({c_arr, std::size(c_arr)});  // 传递指针+长度// std::array<T, N>std::array arr = {11, 22, 33, 44};show(arr);  // 直接传std::array// std::vectorstd::vector vec{111, 222, 333, 444};show(vec);                        // 直接传vectorshow({vec.begin(), vec.end()});   // 首尾迭代器show({vec.begin(), vec.size()});  // 首迭代器+长度// 链表不是连续序列// std::list lst = {1111, 2222, 3333, 4444};// show(list);// std::deque 也不是
}

简单实现

注意本文只是一个简单实现版本

没有考虑各种特殊情况,也没有用 SFINAE(Substitution Failure Is Not An Error)来进行约束。

一些简单的约束方法可以参考 > (C++) 一个例子,了解 SFINAE 从 cpp11 到 cpp20 的核心技巧 - 知乎 (zhihu.com)

Code

直接上代码:

#include <array>
#include <memory>  // cpp20 std::to_address
#include <vector>namespace my {// 只考虑一个模板参数的情况
template <typename Type>
class span final {
private:Type*  ptr  = nullptr;size_t size = 0;public:constexpr span() noexcept                       = default;constexpr span(const span&) noexcept            = default;constexpr span& operator=(const span&) noexcept = default;constexpr  ~span() noexcept                     = default;public:  // 针对具体类型// 数组是无法传递对象,只能传递引用template <size_t N>constexpr span(Type (&arr)[N]) noexcept {this->ptr  = arr;this->size = N;}// 针对std::arraytemplate <size_t N>constexpr span(std::array<Type, N>& arr) noexcept {this->ptr  = arr.data();this->size = N;}// 针对std::vectorconstexpr span(std::vector<Type>& arr) noexcept {this->ptr  = arr.data();this->size = arr.size();}public:  // 针对迭代式 使用cpp20的`std::to_address`可以同时应对迭代器和指针// 首位置和长度template <typename Iter>constexpr span(Iter iter, size_t length) noexcept {this->ptr  = std::to_address(iter);this->size = length;}// 首位置和尾位置template <typename Iter>constexpr span(Iter first, Iter end) noexcept {this->ptr  = std::to_address(first);this->size = end - first;}public:constexpr Type* begin() const noexcept {return this->ptr;}constexpr Type* end() const noexcept {return this->ptr + this->size;}
};
}  // namespace my

测试代码与上文一致:

提示:将该模块的两份代码直接顺序复制到一个文档中即可

#include <iostream>
namespace name = my;
void show(name::span<int> arr) {for (auto&& elem : arr) {std::cout << elem << ' ';}std::cout << std::endl;
}int main() {// 传统的C式数组int c_arr[] = {1, 2, 3, 4};show(c_arr);                      // 直接传数组show({c_arr, std::size(c_arr)});  // 传递指针+长度// std::array<T, N>std::array arr = {11, 22, 33, 44};show(arr);  // 直接传std::array// std::vectorstd::vector vec{111, 222, 333, 444};show(vec);                        // 直接传vectorshow({vec.begin(), vec.end()});   // 首尾迭代器show({vec.begin(), vec.size()});  // 首迭代器+长度
}

这里的主要难点就是对构造函数的实现,如何获得首地址和长度。

大致可以分为两类

  1. 直接引用对象
  2. 传入首位置

直接引用对象

关于std::vector和std::array相对比较方便,也是大家平时直接应用的方式。

而关于数组,很多人并不熟悉怎么操作。

注意Type (&arr)[N]的写法这里必须要套小括号,因为[]的优先级比&高,不然无法获得数组的引用。

std::array和普通数组一样也是使用template <size_t N>。可见具有动态长度的std::vector在这里是最方便的。

public:  // 针对具体类型// 数组是无法传递对象,只能传递引用template <size_t N>constexpr span(Type (&arr)[N]) noexcept {this->ptr  = arr;this->size = N;}// 针对std::arraytemplate <size_t N>constexpr span(std::array<Type, N>& arr) noexcept {this->ptr  = arr.data();this->size = N;}// 针对std::vectorconstexpr span(std::vector<Type>& arr) noexcept {this->ptr  = arr.data();this->size = arr.size();}

传入首位置

首地址+长度是C语言中传数组的最常见形式。

到了C++中泛化出了迭代器的标准。为了同时适配普通指针和迭代器,在C++20中推出了std::to_address()的方法。可以多态的转化到指针中。这样就不用针对指针和迭代器写两个版本了。

关于这里的第二个传入首位位置的版本,需要允许两者直接的相互减。

请注意各种迭代器的区别:迭代器库 - cppreference.com

public:  // 针对迭代式 使用cpp20的`std::to_address`可以同时应对迭代器和指针// 首位置和长度template <typename Iter>constexpr span(Iter iter, size_t length) noexcept {this->ptr  = std::to_address(iter);this->size = length;}// 首位置和尾位置template <typename Iter>constexpr span(Iter first, Iter end) noexcept {this->ptr  = std::to_address(first);this->size = end - first;}



END


http://www.ppmy.cn/news/1252872.html

相关文章

智慧配电房解决方案

智慧配电房解决方案是一种运用物联网、云计算、大数据、人工智能等先进技术&#xff0c;对配电房进行智能化改造和升级的方案。依托电易云-智慧电力物联网&#xff0c;可通过Web及APP&#xff0c;随时随地掌控配电室的运行情况&#xff0c;线上完成有关设备的智能巡检和派单&am…

web前端之css变量的妙用、通过JavaScrip改变css文件中的属性值、querySelector、setProperty

MENU 效果图htmlJavaScripstylequerySelectorsetProperty 效果图 html <div id"idBox" class"p_r w_680 h_160 b_1s_red"><div id"idItem" class"p_a l_0 t_30 w_100 h_100 bc_rgba_255_00_05 radius_50_"></div> …

酒店 KPI绩效考核指标及应用

“路遥知马力&#xff0c;日久见人心”&#xff0c;目前国内各类型酒店风起云涌&#xff0c;大有在市场竞争中一比高下之势&#xff0c;各路精英受经济型酒店低投入高回报的市场利益驱动&#xff0c;都分分抢占市场&#xff0c;从而使国内经济型酒店的数量不断增加&#xff0c;…

用 LangChain 搭建基于 Notion 文档的 RAG 应用

如何通过语言模型查询 Notion 文档&#xff1f;LangChain 和 Milvus 缺一不可。 在整个过程中&#xff0c;我们会将 LangChain 作为框架&#xff0c;Milvus 作为相似性搜索引擎&#xff0c;用二者搭建一个基本的检索增强生成&#xff08;RAG&#xff09;应用。在之前的文章中&a…

【MySQL】MySQL安装 环境初始化

MySQL安装 MYSQL官网 安装完成后,傻瓜下一步即可 配置一下环境变量即可 (1) 初始化MySQL, 管理员身份运行 mysqld --initialize-insecure(2) 注册 mysqld mysqld -install# 如果记录以前的版本执行下面指令 mysqld -remove(3) 启动MySQL服务 // 启动mysql服务 net start …

跨越鸿沟-颠覆性产品营销指南笔记

跨越鸿沟-颠覆性产品营销指南笔记 一、发现鸿沟 一、技术采用生命周期 技术采用生命周期 如果采用一个新产品&#xff0c;我们就得改变一贯的行为模式&#xff0c;或者改变我们依赖的其他产品或服务&#xff0c;那么&#xff0c;我们对技术采用的态度就变得很重要&#xff0c…

带你手搓阻塞队列——自定义实现

&#x1f308;&#x1f308;&#x1f308;今天给大家分享的是——阻塞队列的自定义实现&#xff0c;通过自定义实现一个阻塞队列&#xff0c;可以帮助我们更清晰、更透彻的理解阻塞队列的底层原理。 清风的CSDN博客 &#x1f6e9;️&#x1f6e9;️&#x1f6e9;️希望我的文章…

计算机新建盘符和重新分配盘符的大小

一、新建盘符 有些电脑刚买来时候&#xff0c;只有一个C盘分区&#xff0c;此时最好增加几个分区方便使用。 注意&#xff1a;分区操作要慎重&#xff0c;不要轻易去试。这里只针对购买的电脑厂家未做分区&#xff0c;只有一个C盘的情况。 如果自己电脑的分区本身已经满足你…

CentOS系统环境搭建(二十四)——借助Git实现一键部署

centos系统环境搭建专栏&#x1f517;点击跳转 文章目录 如何一键部署1.后端1.1 拉取代码1.2 打包1.2.1 如果打包没问题&#xff0c;只看这部分即可1.2.2 maven有问题看这一部分1.2.2.1 安装jdk1.2.2.2 Jdk路径寻找&#xff0c;使用命令1.2.2.3 由此可知&#xff0c;jdk路径为…

基于Java SSM简单的学籍管理系统

1. 实验选题 《学籍管理系统》。 2. 实验要求和目的 要求:通过分析《学籍管理系统》中的数据模式&#xff0c;建立相应的数据库&#xff0c;并在此数据库中进行建数据表&#xff0c;插入、更新、查询和删除记录操作&#xff0c;最后用可视化编程工具和关系数据库开发一个简单…

shell编程系列(9)-使用cut选择列

文章目录 前言使用cut选择列选择特定的列 结语 前言 前面的文章介绍了sed命令&#xff0c;sed可以帮我们处理文本列&#xff0c;这边文章介绍cut命令&#xff0c;cut命令可以帮我们选择想要的列&#xff0c;在文本处理时候结合sed命令&#xff0c;就可以精准定位了。 cut命令是…

基于Java SSM移动电源租赁系统

涉及的知识点&#xff1a;Java程序设计基础知识、类的创建、对象的使用、面向对象继承、面向对象多态性、抽象类和接口、集合与泛型、文件与输入输出流操作、异常处理与日志记录、Java GUI 、事件处理、Java数据库编程。 一、实验目的 &#xff08;1&#xff09;掌握Java编程…

微软 Power Platform 零基础 Power Pages 网页搭建教程学习实践进阶以及常见问题解答(二)

微软 Power Platform 零基础 Power Pages 网页搭建教程学习实践进阶及常见问题解答&#xff08;二&#xff09; Power Pages 学习实践进阶 微软 Power Platform 零基础 Power Pages 网页搭建教程学习实践进阶及常见问题解答&#xff08;二&#xff09;Power Pages 核心工具和组…

滴滴崩溃超过12小时,将本增笑?

从 2023 年 11 月 27 日晚上 10 点左右截止 2023 年 11 月 28 日上午 11点左右&#xff0c;目前滴滴出行包括网约车和共享单车等业务已经恢复正常。而此次全面的功能瘫痪持续了接近12小时&#xff0c;也是近年来滴滴出行瘫痪时间最长的一次故障。 2023 年以来&#xff0c;语雀崩…

《ChatGPT实操应用大全》探索无限可能

&#x1f5e3;️探索ChatGPT&#xff0c;开启无限可能&#x1f680; 文末有免费送书福利&#xff01;&#xff01;&#xff01; ChatGPT是人类有史以来最伟大的发明。他能写作、绘画、翻译、看病、做菜、编程、数据分析、制作视频、解高等数学题…&#xff0c;他会的技能…

LeetCode105.从前序和中序遍历序列构造二叉树

这道题看完题想了几分钟就想到大概的思路了&#xff0c;但是在写的时候有很多细节没注意出了很多问题&#xff0c;然后写了1个多小时&#xff0c;其实这道题挺简单的。 首先&#xff0c;最基本的知识&#xff0c;先序遍历是根左右&#xff0c;中序遍历是左根右&#xff0c;那么…

Git——使用Git进行程序开发

主要介绍个人开发提交记录的主要流程&#xff0c;包括以下内容&#xff1a; 索引- 提交的暂存区。查看工作的状态和内部变更。如何读取用于描述变更的已扩展统一diff格式。支持查询和交互的提交&#xff0c;修改提交。创建、显示和选择&#xff08;切换&#xff09;分支。切换…

计算机网络:网络层

0 本节主要内容 问题描述 解决思路 1 问题描述 两大问题&#xff08;重点&#xff0c;也是难点&#xff09;&#xff1a; 地址管理&#xff1b;路由选择。 1.1 子问题1&#xff1a;地址管理 网络上的这些主机和节点都需要使用一种规则来区分&#xff0c;就相当于是一种身…

AH8691是一款80V转3.3V降压芯片

80V转3.3V降压芯片AH8691&#xff1a;宽电压输入范围&#xff0c;高效稳定&#xff0c;多领域应用 在当今电源管理领域&#xff0c;高效、稳定的降压芯片已成为各类设备的关键需求。本文将为您介绍一款80V转3.3V降压芯片AH8691&#xff0c;其具备宽电压输入范围、迟滞控制、高…

模拟退火算法迭代lightGBM最优

模拟退火在迭代最优参数时也有很多问题会出现&#xff0c;比如初始解就在最优值附近&#xff0c;造成的stay_counter参数会反复回零&#xff0c;导致无法快速退出。还有就是T温度参数会很难累计到最小温度以下&#xff0c;导致迟迟无法收敛&#xff0c;可以将初始炉温T_max设置…
最新文章