C++2b(C++23)標準ライブラリに追加される多次元ビューstd::mdspan
クラステンプレート(→id:yohhoy:20230303)の、コンストラクタと推論ガイド(deduction guide)による実引数クラステンプレート推論(CTAD; Class Template Argument Deduction)のあれこれ。
mdspan
型における次元情報std::extents<I, En...>
では、各次元の要素数(En
)はコンパイル時定数/実行時動的指定(std::dynamic_extent
)のいずれかとなる。ほとんどのCTAD*1では動的サイズ(std::dextents<I, R>
)に型推論される。
2024-05-02:C++2c(C++26)ではP3029R1が採択され、整数リストによる要素数リスト指定CTADにて動的/固定サイズ混在が可能となる(C++23時点では動的サイズ固定)。std::submdspan
関数(→id:yohhoy:20240201)へのスライス指定仕様との一貫性向上。
デフォルトコンストラクタ
動的要素数次元を含む場合のみデフォルト構築可能。
using Matrix = std::mdspan<double, std::dextents<size_t, 2>>;
Matrix m1;
using ViewNx3 = std::mdspan<double, std::extents<size_t, std::dynamic_extent, 3>>;
ViewNx3 m2;
using Mat3x3 = std::mdspan<double, std::extents<size_t, 3, 3>>;
Mat3x3 m3;
要素数リスト指定コンストラクタ
全次元の要素数リストまたは動的要素数の次元に対する要素数リストを、整数リスト/std::array<I, N>
/std::span<I, N>
にて指定する。CTAD利用時のインデクス型(mdspan::index_type
)は推論ガイドによりsize_t
となる。
double *ptr = ;
using Matrix = std::mdspan<double, std::dextents<size_t, 2>>;
Matrix m1{ptr, 2, 3};
std::mdspan m2{ptr, 2, 3};
using Mat2x3 = std::mdspan<double, std::extents<size_t, 2, 3>>;
Mat2x3 m3a{ptr};
Mat2x3 m3c{ptr, 2};
Mat2x3 m3b{ptr, 2, 3};
Mat2x3 m3d{ptr, 0, 42};
double *ptr = ;
std::array<int, 2> exts = {2, 3};
using Matrix = std::mdspan<double, std::dextents<size_t, 2>>;
Matrix m1{ptr, exts};
std::mdspan m2{ptr, exts};
double *ptr = ;
std::vector<int> exts_vec = {2, 3};
std::span<int, 2> exts{exts_vec};
using Matrix = std::mdspan<double, std::dextents<size_t, 2>>;
Matrix m1{ptr, exts};
std::mdspan m2{ptr, exts};
std::span<int, std::dynamic_extent> exts2{&exts_list[0], 2};
Matrix m3{ptr, exts2};
std::mdspan m4{ptr, exts2};
C++2c:要素数リスト指定コンストラクタ(CTAD)
2024-05-02追記:C++2c以降は要素数リストにおいて整数定数(std::integral_constant
互換の定数値)を与えると、対象次元の要素数を固定サイズに推論させることができる。
double *ptr = ;
template <int N>
constexpr auto Int = std::integral_constant<int, N>{};
std::mdspan m1{ptr, 2, 3};
std::mdspan m2{ptr, Int<2>, Int<3>};
固定サイズ/動的サイズ変換
固定サイズから動的サイズへは常に安全に変換可能。動的サイズから固定サイズへはサイズ情報が一致していれば変換可能。
double *ptr = ;
using Matrix = std::mdspan<double, std::dextents<size_t, 2>>;
using Mat3x3 = std::mdspan<double, std::extents<size_t, 3, 3>>;
using Mat4x4 = std::mdspan<double, std::extents<size_t, 4, 4>>;
Mat3x3 src3x3{ptr};
Matrix m1{src3x3};
Mat4x4 m2{src3x3};
Matrix srcmat{ptr, 3, 3};
Mat3x3 m3{srcmat};
Mat4x4 m4{srcmat};
C配列型からの変換(CTAD)
1次元C配列型のみ直接変換がサポートされる。2次元以上のC配列型からのmdspan
構築はコンパイルエラーとなるか、要素アクセス時に未定義動作を引き起こす。配列先頭要素ポインタからの変換は意図しない結果をもたらす。
double arr1d[100] = ;
using Array100 = std::mdspan<double, std::extents<size_t, 100>>;
Array100 m1{arr1d};
std::mdspan m2{arr1d};
using ArrayN = std::mdspan<double, std::dextents<size_t, 1>>;
ArrayN m3{arr1d, 100};
std::mdspan m4{arr1d, 100};
std::mdspan m5{&arr1d[0], 100};
std::mdspan m6{&arr1d[0]};
double arr2d[4][4] = ;
using Mat4x4 = std::mdspan<double, std::extents<size_t, 4, 4>>;
Mat4x4 m1{arr2d};
Mat4x4 m2{arr2d, 4, 4};
std::mdspan m3{arr2d};
std::mdspan m4{arr2d, 4, 4};
Mat4x4 m5{&arr2d[0][0]};
std::mdspan m6{&arr2d[0][0], 4, 4};
次元情報指定コンストラクタ(CTAD)
メモリ領域ポインタとstd::extents
/std::dextents
*2を指定すると、任意の次元数(rank)と要素数(extents)をもつmdspan
型を推論可能となる。CTADを用いるメリットは小さいが、第1引数のポインタ型から要素型(mdspan::element_type
)が推論される。
double *ptr = ;
using Extents2D = std::dextents<size_t, 2>;
std::mdspan m1{ptr, Extents2D{2, 3}};
using ExtentsNx3 = std::extents<size_t, std::dynamic_extent, 3>;
std::mdspan m2{ptr, ExtentsNx3{2}};
using Extents2x3 = std::extents<size_t, 2, 3>;
std::mdspan m3{ptr, Extents2x3{}};
メモリレイアウト型変換
行優先(row major)std::layout_right
/列優先(column major)std::layout_left
レイアウトから汎用ストライドstd::layout_stride
レイアウトへは常に安全に変換可能。異種レイアウト間変換においては、マッピングのずらし幅(stride)に互換性があるケースに限って変換可能。
注:std::mdspan
は実データに対する「ビュー」にすぎない。例えば行列の転置(transpose)をmdspan
型変換のみで実現することはできない。
double *ptr = ;
using Extents3x3 = std::extents<size_t, 3, 3>;
using MatCpp = std::mdspan<double, Extents3x3 >;
using MatFortran = std::mdspan<double, Extents3x3, std::layout_left>;
using MatStrided = std::mdspan<double, Extents3x3, std::layout_stride>;
MatCpp src_cpp{ptr};
MatFortran src_fortran{ptr};
MatStrided m1{src_cpp};
MatStrided m2{src_fortran};
MatFortran m3{src_cpp};
MatCpp m4{src_fortran};
std::array<int, 2> strides{3, 1};
std::layout_stride::mapping<Extents3x3> mapping{{}, strides};
MatStrided src_stride{ptr, mapping};
MatCpp m5{src_stride};
MatFortran m6{src_stride};
double *ptr = ;
using Extents1D = std::extents<size_t, 10>;
using ArrayRow = std::mdspan<double, Extents1D, std::layout_right>;
using ArrayCol = std::mdspan<double, Extents1D, std::layout_left>;
ArrayRow src_row{ptr};
ArrayCol src_col{ptr};
ArrayCol m1{src_row};
ArrayRow m2{src_col};
その他のコンストラクタ(CTAD)
前掲以外のCTADとして、下記の実引数からのmdspan
型推論がサポートされる。
- レイアウトマップ
Layout::mapping
- レイアウトマップ
Layout::mapping
, 要素アクセサAccessor
関連URL