yohhoyの日記

技術的メモをしていきたい日記

std::mdspan AccessorPolicy応用例

C++23標準ライブラリの多次元ビューstd::mdspan(→id:yohhoy:20230303)における、第4テンプレートパラメータAcssesorPolicyを用いた要素アクセスカスタマイズの具体事例。

C++2c(C++26)標準ライブラリ採用が決定している線形代数基本アルゴリズム <linalg> ヘッダでは、mdspan参照先のメモリを書換えずに各要素のスケーリング(std::linalg::scaled)や複素共役(std::linalg::conjugated)変換を行うビューを提供する。適用後の多次元ビュー要素は読み取り専用となる。

#include <mdspan>
#include <linalg>  // C++2c(C++26)

using Vec = std::mdspan<int, std::dextent<size_t, 1>>;
int arr[] = {1, 2, 3};

Vec vec1{ arr };
assert(vec1[0] == 1);
// 要素アクセス vec1[i] はint&型を返す
// &(vec1[i]) == &(arr[i])

// 全要素を2倍した1次元ビュー
auto vec2 = std::linalg::scaled(2, vec1);
assert(vec2[0] == 2 && arr[0] == 1);
// 要素アクセス vec2[i] はint型を返すため
// 要素書き換え vec2[0] = 42; はill-formed

提案文書P1673R13 Wordingより一部引用(クラス宣言は簡略化)。

1 The class template scaled_accessor is an mdspan accessor policy which upon access produces scaled elements. reference. It is part of the implementation of scaled [linalg.scaled.scaled].

template<class ScalingFactor, class NestedAccessor>
class scaled_accessor {
public:
  using element_type = add_const_t<
    decltype(declval<ScalingFactor>() * declval<NestedAccessor::element_type>())>;
  using reference = remove_const_t<element_type>;
  using data_handle_type = NestedAccessor::data_handle_type;
  using offset_policy = /*...*/;

  constexpr scaled_accessor(const ScalingFactor& s, const NestedAccessor& a);
  constexpr reference access(data_handle_type p, size_t i) const;
  // ...
};

1 The scaled function template takes a scaling factor alpha and an mdspan x, and returns a new read-only mdspan with the same domain as x, that represents the elementwise product of alpha with each element of x.

template<class ScalingFactor,
         class ElementType,
         class Extents,
         class Layout,
         class Accessor>
constexpr auto scaled(
  ScalingFactor alpha,
  mdspan<ElementType, Extents, Layout, Accessor> x);

2 Let SA be scaled_accessor<ScalingFactor, Accessor>
3 Returns:

mdspan<typename SA::element_type, Extents, Layout, SA>(
  x.data_handle(), x.mapping(), SA(alpha, x.accessor()))

メモ:<linalg> ヘッダではこのほかに行列転置(std::linalg::transposed)や複素共役転置(std::linalg::conjugate_transposed)変換を行うビューも提供する。行列転置はmdspanの第3テンプレートパラメータLayoutPolicyを利用して実現される。

関連URL