多维array和多维视图std::mdspan

多维数组

这个特性用于访问多维数组,之前C++ operator[] 只支持访问单个下标,无法访问多维数组。
因此要访问多维数组,以前的方式是:

  • 重载operator(),于是能够以m(1, 2) 来访问第1 行第2 个元素。但这种方式容易和函数调
    用产生混淆;
  • 重载operator[],并以std::initializer_list 作为参数,然后便能以m[{1, 2}] 来访问元
    素。但这种方式看着别扭;
  • 链式链接operator[],然后就能够以m[1][2] 来访问元素。C语言二维数组的访问方式;
  • 定义一个at() 成员,然后通过at(1, 2) 访问元素。同样不方便。

在C++23,我们终于可以通过m[1, 2] 这种方式来访问多维数组。一个例子

template <class T, size_t R, size_t C>
struct matrix 
{T& operator[](const size_t r, const size_t c) noexcept {return data_[r * C + c];}const T& operator[](const size_t r, const size_t c) const noexcept {return data_[r * C + c];}private:
std::array<T, R * C> data_;
};int main() 
{matrix<int, 2, 2> m;m[0, 0] = 0;m[0, 1] = 1;m[1, 0] = 2;m[1, 1] = 3;for (auto i = 0; i < 2; ++i) {for (auto j = 0; j < 2; ++j) {std::cout << m[i, j] << ' ';}std::cout << std::endl;}
}

std::mdspan

std::mdspan(多维数组视图)是 C++23 新增的非拥有多维数组的视图,用于表示连续对象序列,允许灵活操作多维数据,支持动态维度。 这个连续对象序列可以是一个简单的 C 数组、带有大小的指针、std::arraystd::vector 或 std::stringmdspan 是一种轻量级的多维数组视图,不持有数据,而是提供了对现有数据的多维访问方式。它结合了指针和多维索引的优点,使得数据访问更加高效和灵活。

在标头 <mdspan> 定义

template<

    class T,
    class Extents,
    class LayoutPolicy = std::layout_right,
    class AccessorPolicy = std::default_accessor<T>

> class mdspan;

模板形参

T-元素类型;既不是抽象类也不是数组类型的完整对象类型。
Extents-指定维数及各维大小,均为编译时已知。必须是 std::extents 的特化。
LayoutPolicy-指定如何将多维索引转换为底层的一维索引(列优先三维数组、对称三角二维矩阵等)。必须满足布局映射策略 (LayoutMappingPolicy) 。
AccessorPolicy-指定如何将底层一维索引转换为对 T 的引用。必须满足 std::is_same_v<T, typename AccessorPolicy​::​element_type> 为 true 的约束条件。必须满足访问器策略 (AccessorPolicy)


由于 C++17 中的类模板参数推导(CTAD),编译器通常可以自动从初始化器的类型推导出模板参数。

成员函数

(构造函数)

构造一个 mdspan
(公开成员函数)

operator=

给一个 mdspan 赋值
(公开成员函数)
元素访问

operator[]

访问指定多维索引处的元素
(公开成员函数)
观察器

size

返回多维索引空间的大小
(公开成员函数)

empty

检查索引空间大小是否为零
(公开成员函数)

stride

获取沿指定维度的步长
(公开成员函数)

extents

获取范围(extent)对象
(公开成员函数)

data_handle

获取指向底层一维序列的指针
(公开成员函数)

mapping

获取映射(mapping)对象
(公开成员函数)

accessor

获取访问器策略对象
(公开成员函数)

is_unique

确定此 mdspan 的映射是否唯一(每个索引组合映射到不同的基础元素)
(公开成员函数)

is_exhaustive

确定此 mdspan 的映射是否详尽(exhaustive)(可以使用某些索引组合访问每个底层元素)
(公开成员函数)

is_strided

确定此 mdspan 的映射是否跨步(在每个维度中,每次递增索引都会跳过相同数量的基础元素)
(公开成员函数)

is_always_unique

[静态]

确定此 mdspan 的布局映射是否始终唯一(unique)
(公开静态成员函数)

is_always_exhaustive

[静态]

确定此 mdspan 的布局映射(layout mapping)是否总是详尽的
(公开静态成员函数)

is_always_strided

[静态]

确定此 mdspan 的布局映射是否始终跨步(strided)
(公开静态成员函数)

非成员函数

std::swap(std::mdspan)

(C++23)

针对 mdspan 特化的 std::swap 算法
(函数模板)
子视图

submdspan

(C++26)

返回现存 mdspan 的子集上的视图
(函数模板)

submdspan_extents

(C++26)

从现存 extents 和分片说明符创建新的 extents
(函数模板)

辅助类型和模板

extents

(C++23)

某秩多维索引空间的一个描述符
(类模板)

dextentsdims

(C++23)(C++26)

全动态 std::extents 的方便别名模板
(别名模板)

default_accessor

(C++23)

指示索引访问 mdspan 元素的方式的类型
(类模板)

aligned_accessor

(C++26)

提供按对齐访问 mdspan 成员的类型
(类模板)
布局映射策略

layout_left

(C++23)

列优先多维数组布局映射策略;最左边的尺度具有步幅 1
(类)

layout_right

(C++23)

行优先多维数组布局映射策略;最右边的尺度具有步幅 1
(类)

layout_stride

(C++23)

具有用户自定义步长的布局映射策略
(类)

layout_left_padded

(C++26)

具有可大于或等于最左侧尺度的填充跨步的列主序布局映射策略
(类)

layout_right_padded

(C++26)

具有可大于或等于最右侧尺度的填充跨步的行主序布局映射策略
(类)
子视图辅助项

full_extentfull_extent_t

(C++26)

切片说明符标签,描述指定尺度的全部索引范围。
(标签)

strided_slice

(C++26)

切片说明符,表示一组按照偏移量、尺度和跨步三值指定的有规律分布的索引
(类模板)

submdspan_mapping_result

(C++26)

各 submdspan_mapping 重载的返回类型
(类模板)

示例 1:基本使用

#include<iostream>
#include <mdspan>int main() {// 一维连续存储(模拟二维数组)int data[] = {1,2,3,4,5,6,7,8,9,10,11,12};// 创建 3x4 的二维视图std::mdspan mat(data, 3, 4); // 访问元素 [行][列]std::cout << mat[1][2]; // 输出 7// 修改元素mat[2][3] = 42; // 修改第3行第4列的元素
}

示例 2:动态维度 

#include <mdspan>
#include <vector>int main() {std::vector<int> data(20); // 动态存储// 创建 4x5 的动态视图std::mdspan<int, std::dextents<2>> dyn_span(data.data(), 4, 5);dyn_span[3][4] = 100; // 最后一元素
}

3. 多维数组的布局策略

mdspan 允许指定内存布局,优化访问模式:

  • 行优先(C风格)std::layout_right(默认)

  • 列优先(Fortran风格)std::layout_left

#include <mdspan>int main() {int data[12] = { /* ... */ };// 列优先的 3x4 视图std::mdspan<int, std::extents<3,4>, std::layout_left> col_major(data);
}

4. 切片与子视图

mdspan 支持通过切片操作获取子视图:

auto sub_view = std::submdspan(mat, 1, std::full_extent); // 获取第2行所有列
std::cout << sub_view[2]; // 输出原数组的 mat[1][2]

5. 结合算法使用

多维数组与标准库算法协同工作:

#include <algorithm>
#include <mdspan>int main() 
{int data[3][4] = { /* ... */ };std::mdspan mat(data);// 遍历所有元素std::for_each(mat.data_handle(), mat.data_handle() + mat.size(), [](int& x) { x *= 2; });
}

5. 布局策略

std::mdspan 允许您指定用于访问底层内存的布局策略。默认情况下,使用 std::layout_right(C、C++ 或 Python 风格),但您也可以指定 std::layout_left(Fortran 或 MATLAB 风格)。

使用布局策略std::mdspanstd::layout_right遍历两个std::layout_left可以看出差异。

#include <mdspan>
#include <iostream>
#include <vector>int main() 
{std::vector myVec{1, 2, 3, 4, 5, 6, 7, 8};std::mdspan<int, std::extents<std::size_t,      // (1)std::dynamic_extent, std::dynamic_extent>, std::layout_right> m{myVec.data(), 4, 2};std::cout << "m.rank(): " << m.rank() << '\n';for (std::size_t i = 0; i < m.extent(0); ++i) {for (std::size_t j = 0; j < m.extent(1); ++j) {std::cout << m[i, j] << ' ';  }std::cout << '\n';}std::cout << '\n';std::mdspan<int, std::extents<std::size_t,     // (2)std::dynamic_extent, std::dynamic_extent>, std::layout_left> m2{myVec.data(), 4, 2};std::cout << "m2.rank(): " << m2.rank() << '\n';for (std::size_t i = 0; i < m2.extent(0); ++i) {for (std::size_t j = 0; j < m2.extent(1); ++j) {std::cout << m2[i, j] << ' ';  }std::cout << '\n';}}

6. 性能与注意事项

  • 连续存储mdspan 不管理内存,需确保底层数据连续。

  • 边界检查:默认无越界检查,可通过自定义策略添加。

  • 灵活性:适用于科学计算、图像处理等需要多维数据的场景。


总结

  • 传统多维数组:适合静态、编译时已知维度的场景。

  • std::mdspan:提供动态维度、灵活布局和高效访问,是 C++23 处理多维数据的现代方式。

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

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

相关文章

Python标准库之os模块常用方法

一、os模块简介 os模块是Python标准库中与操作系统交互的一个重要模块。它提供了非常丰富的方法来处理文件、目录以及与操作系统相关的操作&#xff0c;让我们可以编写跨平台的代码&#xff0c;无论是在Windows、Linux还是macOS系统上都能运行。 二、文件和目录操作 获取当前…

利用AI让数据可视化

1. 从问卷星上下载一份答题结果。 序号用户ID提交答卷时间所用时间来源来源详情来自IP总分1、《中华人民共和国电子商务法》正式实施的时间是&#xff08;&#xff09;。2、&#xff08;&#xff09;可以判断企业在行业中所处的地位。3、&#xff08;&#xff09;是指店铺内有…

K8S学习之基础三十五:k8s之Prometheus部署模式

Prometheus 有多种部署模式&#xff0c;适用于不同的场景和需求。以下是几种常见的部署模式&#xff1a; 1. 单节点部署 这是最简单的部署模式&#xff0c;适用于小型环境或测试环境。 特点&#xff1a; 单个 Prometheus 实例负责所有的数据采集、存储和查询。配置简单&…

【第14节】windows sdk编程:进程与线程介绍

目录 一、进程与线程概述 1.1 进程查看 1.2 何为进程 1.3 进程的创建 1.4 进程创建实例 1.5 线程查看 1.6 何为线程 1.7 线程的创建 1.8 线程函数 1.9 线程实例 二、内核对象 2.1 何为内核对象 2.2 内核对象的公共特点 2.3 内核对象句柄 2.4 内核对象的跨进程访…

Python简单爬虫实践案例

学习目标 能够知道Web开发流程 能够掌握FastAPI实现访问多个指定网页 知道通过requests模块爬取图片 知道通过requests模块爬取GDP数据 能够用pyecharts实现饼图 能够知道logging日志的使用 一、基于FastAPI之Web站点开发 1、基于FastAPI搭建Web服务器 # 导入FastAPI模…

uniapp工程中解析markdown文件

在uniapp中如何导入markdown文件&#xff0c;同时在页面中解析成html&#xff0c;请参考以下配置&#xff1a; 1. 安装以下3个依赖包 npm install marked highlight.js vite-plugin-markdown 2. 创建vite.config.js配置文件 // vite.config.js import { defineConfig } fro…

sass介绍

1、Sass简介 Sass 是一种 CSS 的预编译语言。它提供了 变量&#xff08;variables&#xff09;、嵌套&#xff08;nested rules&#xff09;、 混合&#xff08;mixins&#xff09;、 函数&#xff08;functions&#xff09;等功能&#xff0c;并且完全兼容 CSS 语法。Sass 能…

[JavaScript]如何利用作用域块避免闭包内存泄漏?

出自《你不知道的JavaScript》上卷 以下是本书给出的反例: function process (data) {...} var bigdata{...} process(bigdata); var btn document.getElementById(x); btn.addEventListener(click, function click{...});click会被回调在其他位置, 在addEventListener函数内…

leetcode hot100(五)

11. 盛最多水的容器 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。 说明&#xff1a;你…

Unity 云渲染本地部署方案

Unity Render Streaming 云渲染环境搭建 0.安装 Unity Render Streaming 实现原理: 服务器与客户端实现功能包括: 详细内容见官方文档&#xff1a; 官方文档: https://docs.unity3d.com/Packages/com.unity.renderstreaming3.1/manual/tutorial.html Unity 流送云渲染介绍: …

洛谷 P3986 斐波那契数列

P3986 斐波那契数列 题目描述 定义一个数列&#xff1a; f ( 0 ) a , f ( 1 ) b , f ( n ) f ( n − 1 ) f ( n − 2 ) f(0) a, f(1) b, f(n) f(n - 1) f(n - 2) f(0)a,f(1)b,f(n)f(n−1)f(n−2) 其中 a, b 均为正整数&#xff0c;n ≥ 2。 问有多少种 (a, b)&…

【java面型对象进阶】------继承实例

继承结构下的标准Javabean 代码如下&#xff1a; package demo10;//定义员工父类 public class Employee {private String id;private String name;private double salary;//构造方法public Employee(){}public Employee(String id,String name,double salary){this.idid;thi…

Vitis 2024.1 无法正常编译custom ip的bug(因为Makefile里的wildcard)

现象&#xff1a;如果在vivado中&#xff0c;添加了自己的custom IP&#xff0c;比如AXI4 IP&#xff0c;那么在Vitis&#xff08;2024.1&#xff09;编译导出的原本的.xsa的时候&#xff0c;会构建build失败。报错代码是&#xff1a; "Compiling blank_test_ip..."…

【图论】并查集的学习和使用

目录 并查集是什么&#xff1f; 举个例子 组成 父亲数组&#xff1a; find函数&#xff1a; union函数&#xff1a; 代码实现&#xff1a; fa[] 初始化code: find code&#xff1a; 递归实现: 非递归实现: union code : 画图模拟&#xff1a; 路径压缩&#xff1a…

Java使用FFmpegFrameGrabber进行视频拆帧,结合Thumbnails压缩图片保存到文件夹

引入依赖 <dependency><groupId>net.coobird</groupId><artifactId>thumbnailator</artifactId><version>0.4.17</version></dependency><dependency><groupId>org.bytedeco</groupId><artifactId>ja…

mysql与redis的日志策略

MySQL 和 Redis 在日志记录方面采用了不同的策略&#xff0c;分别对应写前日志&#xff08;Write-Ahead Logging, WAL&#xff09;和写后日志&#xff08;Write-After Logging&#xff09;。以下是它们的详细说明&#xff1a; 1. MySQL&#xff1a;写前日志&#xff08;Write-A…

nacos安装,服务注册,服务发现,远程调用3个方法

安装 点版本下载页面 服务注册 每个微服务都配置nacos的地址&#xff0c;都要知道 服务发现 2个是知道了解 远程调用基本实现 远程调用方法2&#xff0c;负载均衡API测试 远程调用方法3&#xff0c;注解 负载均衡的远程调用&#xff0c; 总结 面试题

Ubuntu Qt: no service found for - “org.qt-project.qt.mediaplayer“

1、前言 在一次项目过程中&#xff0c;因项目需求&#xff0c;需要将windows开发的Qt项目迁移到ubuntu系统中&#xff0c;且在某个功能项中需要播放音频&#xff0c;在windows系统中能够正常运行&#xff0c;但在ubuntu系统中却显示defaultServiceProvider::requestService(): …

Blender制作次表面材质

效果: 主要用沃罗诺伊纹理做出云絮感 然后EV开启次表面设置

用 pytorch 从零开始创建大语言模型(四):从零开始实现一个用于生成文本的GPT模型

从零开始创建大语言模型&#xff08;Python/pytorch &#xff09;&#xff08;四&#xff09;&#xff1a;从零开始实现一个用于生成文本的GPT模型 4 从零开始实现一个用于生成文本的GPT模型4.1 编写 L L M LLM LLM架构4.2 使用层归一化对激活值进行标准化4.3 使用GELU激活函数…