最終更新日時: 2026年07月22日 16時08分23秒 (JST)
rotarymars が更新

履歴 編集

function template
<mdspan>

# std::submdspan(C++26)

    namespace std {
      template<
        class ElementType,
        class Extents,
        class LayoutPolicy,
        class AccessorPolicy,
        class... SliceSpecifiers>
      constexpr auto submdspan(
        const mdspan<ElementType, Extents, LayoutPolicy, AccessorPolicy>& src,
        SliceSpecifiers... slices) -> see below;
    }

## 概要

多次元配列ビュー`mdspan`と各次元からの要素取り出し（スライス）方式を指定して、メモリブロックに対する新しい多次元配列ビュー`mdspan`を取得する。

### スライス指定

各次元からの要素取り出し方式は、下記の4種類をサポートする。 スライス指定子リスト`slices...`にインデクス値指定が含まれる場合、戻り値の次元数(rank)は元の多次元配列ビューに対してインデクス値指定した次元数だけ削減される。

- インデクス値指定 : 整数値。指定次元に対する多次元インデクス値を固定する。
- インデクス範囲指定 : インデクス・ペア互換型の値。開始位置(begin)と終了位置(end)で表現される半開区間から要素群を取り出す。
- ストライド・スライス指定 : `std::strided_slice`の値。オフセット(offset)と要素数(extent)とストライド幅(stride)で指定される要素群を取り出す。
- 全要素指定 : `std::full_extent`。指定次元の全要素を取り出す。

### カスタマイゼーションポイント

`submdspan`関数により`mdspan`から部分ビューを取り出すには、変換元のレイアウトマッピング型がカスタマイゼーションポイント`submdspan_mapping`を実装している必要がある。

## テンプレートパラメータ制約

- `sizeof...(slices)`が`Extents::rank()`と等しい
- `LayoutPolicy::mapping<Extents>`が`sliceable-mapping`のモデルである。

## 適格要件

`src`の各次元インデクス`k`に対して、

- `SliceSpecifiers...[k]`が`index_type`の`submdspan`スライス型であり、かつ
- `decltype(slices...[k])`が`Extents`のk番目の次元に対する有効な`submdspan`スライス型であること。

## 事前条件

`src.extents()`の各次元インデクス`k`に対して、`slices...[k]`が`src.extents()`のk番目の要素に対する有効な`submdspan`スライスであること。

## 効果

以下と等価

    auto [...slices] = submdspan_canonicalize_slices(src, raw_slices...);
    auto sub_map_offset = submdspan_mapping(src.mapping(), slices...);
    return mdspan(src.accessor().offset(src.data_handle(), sub_map_offset.offset),
                  sub_map_offset.mapping,
                  typename AccessorPolicy::offset_policy(src.accessor()));

## 例

### 基本的な使い方

    #include <mdspan>
    #include <numeric>
    #include <print>
    #include <string_view>
    #include <utility>

    // 2次元配列ビュー(Matrix)の要素表示
    template <class T, class E, class L, class A>
    void print_mat(std::string_view name, const std::mdspan<T, E, L, A>& mat)
    {
      static_assert(mat.rank() == 2);
      std::println("{}:", name);
      for (size_t i = 0; i < mat.extent(0); ++i) {
        for (size_t j = 0; j < mat.extent(1); ++j) {
          std::print(" {:2}", mat[i, j]);
        }
        std::println("");
      }
    }

    // 1次元配列ビュー(Vector)の要素表示
    template <class T, class E, class L, class A>
    void print_vec(std::string_view name, const std::mdspan<T, E, L, A>& mat)
    {
      static_assert(mat.rank() == 1);
      std::println("{}:", name);
      for (size_t i = 0; i < mat.extent(0); ++i) {
        std::print(" {:2}", mat[i]);
      }
      std::println("");
    }

    int main()
    {
      int arr[20];
      std::ranges::iota(arr, 1);

      // 4x5要素の2次元配列ビュー
      using Ext2D = std::dextents<size_t, 2>;
      std::mdspan mat{arr, Ext2D{4, 5}};
      print_mat("mat", mat);

      // インデクス値指定＋全要素指定（2次元→1次元）
      auto row1 = std::submdspan(mat, 1, std::full_extent);
      print_vec("mat[1,M]", row1);
      auto col2 = std::submdspan(mat, std::full_extent, 2);
      print_vec("mat[N,2]", col2);

      // 単一要素の取り出し（2次元→0次元）
      auto elem = std::submdspan(mat, 1, 2);
      std::println("mat[1,2]:\n {:2}", elem[]);

      // インデクス範囲指定
      auto submat = std::submdspan(mat, std::pair{1,3}, std::pair{1,4});
      print_mat("submat", submat);

      // ストライド・スライス指定
      auto strided = std::submdspan(mat,
        std::strided_slice{.offset=1, .extent=3, .stride=2},
        std::strided_slice{.offset=0, .extent=5, .stride=2});
      print_mat("strided", strided);
    }

#### 出力

    mat:
      1  2  3  4  5
      6  7  8  9 10
     11 12 13 14 15
     16 17 18 19 20
    mat[1,M]:
      6  7  8  9 10
    mat[N,2]:
      3  8 13 18
    mat[1,2]:
      8
    submat:
      7  8  9
     12 13 14
    strided:
      6  8 10
     16 18 20

### 静的要素数mdspanの生成

    #include <concepts>
    #include <mdspan>
    #include <numeric>
    #include <print>
    #include <utility>

    // 整数定数型
    template <int N>
    constexpr auto Int = std::integral_constant<int, N>{};

    int main()
    {
      int arr[20];
      std::ranges::iota(arr, 1);

      // 静的要素数 4x5 の2次元配列ビュー
      std::mdspan mat{arr, std::extents<size_t, 4, 5>{}};

      // 動的要素数 2x3 の2次元配列部分ビューを取り出し
      auto submat_dyn = std::submdspan(mat,
        std::pair{1, 3},
        std::strided_slice{.offset=0, .extent=5, .stride=2});
      static_assert(std::same_as<decltype(submat_dyn)::extents_type, std::dextents<size_t, 2>>);
      std::println("submat_dyn {}x{}", submat_dyn.extent(0), submat_dyn.extent(1));

      // 静的要素数 2x3 の2次元配列部分ビューを取り出し
      auto submat_2x3 = std::submdspan(mat,
        std::pair{Int<1>, Int<3>},
        std::strided_slice{.offset=0, .extent=Int<5>, .stride=Int<2>});
      // (strided_slice::offset は戻り値型に影響を与えない)
      static_assert(std::same_as<decltype(submat_2x3)::extents_type, std::extents<size_t, 2, 3>>);
      std::println("submat_2x3 {}x{}", submat_2x3.extent(0), submat_2x3.extent(1));
    }

#### 出力

    submat_dyn 2x3
    submat_2x3 2x3

### レイアウトマッピング互換性

    #include <mdspan>
    #include <utility>

    int main()
    {
      int arr[6] = {1, 2, 3, 4, 5, 6};

      // 3x2要素の2次元配列ビュー（行優先レイアウト）
      std::mdspan mat{arr, std::extents<size_t, 3, 2>{}};
      static_assert(std::same_as<decltype(mat)::layout_type, std::layout_right>);
      // 1 2
      // 3 4
      // 5 6

      // 行優先レイアウト std::layout_right を維持
      auto row0 = std::submdspan(mat, 0, std::full_extent);
      // 1 2
      auto row12 = std::submdspan(mat, std::pair{1,3}, std::full_extent);
      // 3 4
      // 5 6
      static_assert(std::same_as<decltype(row0)::layout_type, std::layout_right>);
      static_assert(std::same_as<decltype(row12)::layout_type, std::layout_right>);

      // 汎用ストライド指定レイアウト std::layout_stride に変換
      auto col1 = std::submdspan(mat, std::full_extent, 1);
      // 2 4 6
      auto row02 = std::submdspan(mat,
        std::strided_slice{.offset=0, .extent=3, .stride=2},
        std::full_extent);
      // 1 2
      // 5 6
      static_assert(std::same_as<decltype(col1)::layout_type, std::layout_stride>);
      static_assert(std::same_as<decltype(row02)::layout_type, std::layout_stride>);
    }

#### 出力

## バージョン

### 言語

- C++26

### 処理系

- Clang: ??
- GCC: ??
- ICC: ??
- Visual C++: ??

## 関連項目

- `mdspan`
- `full_extent`
- `strided_slice`

## 参照

- P2630R4 Submdspan
- P3663R3 Future-proof `submdspan_mapping`