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;
  • 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';
  }

}

使用静态和动态扩展

#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';

}

布局策略

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

C++23:多维视图(std::mdspan)_第1张图片

使用布局策略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';
  }

}

C++23:多维视图(std::mdspan)_第2张图片

接口概览

  • 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)

你可能感兴趣的:(c++23,c++,算法)