C++2c(C++26)標準ライブラリに追加される多次元部分ビューstd::submdspanについて。
namespace std {
template<
class T, class E, class L, class A,
class... SliceSpecifiers>
constexpr auto submdspan(
const mdspan<T, E, L, A>& src,
SliceSpecifiers... slices) -> ;
}
まとめ:
- 多次元ビュー
std::mdspan(→id:yohhoy:20230303)から部分ビューを取り出す(slice)関数。
- 第2引数以降のスライス指定子リストにより、各次元のスライス方法(slicing)を指定する。
- 単一インデクス:単一値。指定次元に対するインデクスを固定し、次元数(rank)を1つ削減する。*1
- インデクス範囲:開始位置(begin)+終了位置(end)の組。
std::pairや2要素std::tuple等により*2、指定次元に対するインデクス範囲を取り出す。
- ストライド指定範囲:オフセット位置(offset)+要素数(extent)+ストライド(stride)の組。
std::strided_sliceにより、指定次元に対してずらし幅指定したインデクス範囲を取り出す。
- 全選択:タグ値
std::full_extent。指定次元をそのまま取り出す。
- “ストライド指定範囲” 用の
strided_slice型は名前付き初期化(→id:yohhoy:20170820)をサポートする。
- 類似機能を提供するPython/numpyやMatlab/Fortranと異なり、変換元における終了位置(end)ではなく変換元における要素数(extent)による指定を行う。
- 例:
strided_slice{.offset=1, .extent=10, .stride=3}またはstrided_slice{1, 10, 3}
- 2024-12-08追記:提案文書P3355R1が採択され、stride==1な
strided_sliceはunit-stride sliceとして特別扱いされる。インデクス範囲指定や全選択(std::full_extent)と同様にレイアウトポリシー維持基準に影響する。
- “インデクス範囲” および “ストライド指定範囲” で用いるインデクス値は、通常の値と整数定数の2種類をサポートする。*3
- 戻り値型
mdspan<T,E,L,A>における多次元インデクス型Eの各次元要素数に影響を与える。
- 通常の値:整数リテラル(literal)*4を含む整数値。指定次元の要素数は実行時(
std::dynamic_extent)に決定する。
- 整数定数:
std::integral_constant互換の定数値*5。指定次元の要素数はコンパイル時に決定する。
- レイアウトポリシー
LはC++標準ライブラリ提供メモリレイアウトのみサポートする。
- 2024-08-05追記:
std::layout_right_padded, std::layout_left_padded追加にともない変換ルールも複雑化している。更新版は id:yohhoy:20240805 参照。
std::layout_right:同ポリシー型を維持できる場合はlayout_rightを利用。それ以外はlayout_strideへ変換。
std::layout_left:同ポリシー型を維持できる場合はlayout_leftを利用。それ以外はlayout_strideへ変換。
std::layout_stride:layout_strideのまま。
- ユーザ定義レイアウトポリシーをサポートするには、カスタマイズポイント
submdspan_mapping関数を実装する。カスタマイズポイント実装は必須要件ではないが、汎用のフォールバック実装は提供されない。
- 要素型
TとアクセスポリシーAは原則維持される。*6
スライス指定の例
int a[15];
std::ranges::iota(a, 1);
std::mdspan m0{a, std::extents<size_t, 3, 5>{}};
auto m1 = std::submdspan(m0, 1, std::full_extent);
auto m2 = std::submdspan(m0, std::full_extent, 2);
auto m3 = std::submdspan(m0, 1, 2);
auto m4d = std::submdspan(m0, std::pair{1,2}, std::tuple{1,3});
auto m5d = std::submdspan(m0,
std::strided_slice{.offset=0, .extent=3, .stride=2},
std::strided_slice{.offset=1, .extent=4, .stride=3});
template <int N>
constexpr auto Int = std::integral_constant<int, N>{};
auto m4s = std::submdspan(m0,
std::pair{Int<1>,Int<2>}, std::tuple{Int<1>,Int<3>});
auto m5s = std::submdspan(m0,
std::strided_slice{.offset=0, .extent=Int<3>, .stride=Int<2>},
std::strided_slice{.offset=1, .extent=Int<4>, .stride=Int<3>});
2025-09-11追記:上記コードの整数定数Int<N>は、C++2c採択済みP2781R9で追加されるstd::cw<N>にて代替可能。
レイアウトポリシー変換
int a[60] = ;
std::mdspan m0{a, std::extents<size_t, 3, 4, 5>{}};
auto m1 = std::submdspan(m0, 1, std::full_extent, std::full_extent);
auto m2 = std::submdspan(m0, 1, std::pair{1,2}, std::full_extent);
auto m3 = std::submdspan(m0, 1, 0, 2);
auto m4 = std::submdspan(m0, std::full_extent, 0, std::full_extent);
using Exts3x4x5 = std::extents<size_t, 3, 4, 5>;
std::array strides = {20, 1, 4};
auto mapping = std::layout_stride::mapping{Exts3x4x5{}, strides};
std::mdspan m0s{a, mapping};
auto m5 = std::submdspan(m0s, 0, std::full_extent, 0);
assert(m5.mapping().stride(0) == 1);
std::mdspan<int, std::extents<size_t, 4>> m5r{ m5 };
関連URL