• C++23:多维视图(std::mdspan)


    C++23:多维视图(std::mdspan)

    介绍

    在 C++23 中,std::mdspan 是一个非拥有的多维视图,用于表示连续对象序列。这个连续对象序列可以是一个简单的 C 数组、带有大小的指针、std::arraystd::vectorstd::string

    这种多维视图通常被称为多维数组

    多维数组的形状由其维数(也称为秩)和每个维度的大小(也称为扩展)决定。std::mdspan 的大小是所有非零维度的大小的乘积。你可以使用多维索引运算符 [] 来访问 std::mdspan 的元素。

    每个 std::mdspan 的维度可以有静态或动态的扩展。静态扩展意味着其长度在编译时指定;动态扩展意味着其长度在运行时指定。

    定义

    template<
        class T,
        class Extents,
        class LayoutPolicy = std::layout_right,
        class AccessorPolicy = std::default_accessor<T>
    > class mdspan;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • T: 连续对象序列的类型。
    • Extents: 指定维数及其大小;每个维度可以有静态或动态的扩展。
    • LayoutPolicy: 指定用于访问底层内存的布局策略。
    • AccessorPolicy: 指定如何引用底层元素。

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

    示例

    使用动态扩展

    #include 
    #include 
    #include 
    
    int main() {
        
        std::vector myVec{1, 2, 3, 4, 5, 6, 7, 8};          // (1)
    
        std::mdspan m{myVec.data(), 2, 4};                  // (2)
        std::cout << "m.rank(): " << m.rank() << '\n';      // (4)
    
        for (std::size_t i = 0; i < m.extent(0); ++i) {     // (6)
            for (std::size_t j = 0; j < m.extent(1); ++j) { // (7)
                std::cout << m[i, j] << ' ';                // (8)
            }
            std::cout << '\n';
        }
    
        std::cout << '\n';
    
        std::mdspan m2{myVec.data(), 4, 2};                 // (3)
        std::cout << "m2.rank(): " << m2.rank() << '\n';    // (5)
    
        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';
      }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    使用静态和动态扩展

    #include 
    #include 
    #include 
    #include 
    #include 
    
    int main() {
        
        std::vector myVec{1, 2, 3, 4, 5, 6, 7, 8};
    
        std::mdspan<int, std::extents<std::size_t, 2, 4>> m{myVec.data()}; // (1)
        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, std::dynamic_extent, 
                    std::dynamic_extent>> m2{myVec.data(), 4, 2};          // (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';
      }
    
       std::cout << '\n';
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    布局策略

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

    img

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

    #include 
    #include 
    #include 
    
    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';
      }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    img

    接口概览

    • md[ind]: 访问第 ind 个元素。
    • md.size: 返回多维数组的大小。
    • md.rank: 返回多维数组的维数。
    • md.extents(i): 返回第 i 个维度的大小。
    • md.data_handle: 返回指向连续元素序列的指针。

    参考:MC++ BLOG – Der Blog für Modernes C++ von Rainer Grimm (modernescpp.com)

  • 相关阅读:
    MySQL数据库忘记密码之修改密码
    西安华为od前端-机试
    警告。如何防止别人“盗窃” 你的 WiFI 密码,6 个方案提供给你~
    Jupyter notebook 添加目录插件
    使用GSAP制作动画视频
    乱花渐欲迷人眼?这几招教你正确挑选适合自己的建模软件
    示例:WPF中DataGrid设置多级分组样式
    桌面运维命令
    怎样给黑白照片上色?这篇文章来教你
    迎战阿里诸神,庚顿喜提智能制造全球总决赛第三名
  • 原文地址:https://blog.csdn.net/qq_42896106/article/details/133991895