P0009R7
mdspan: A Non-Owning Multidimensional Array Reference

Draft Proposal,

This version:
wg21.link/P0009r7
Issue Tracking:
GitHub
Authors:
Audience:
LWG
Project:
ISO JTC1/SC22/WG21: Programming Language C++
Source:
github.com/ORNL/cpp-proposals-pub/blob/master/P0009/P0009.bs

1. Revision History

1.1. P0009r7: Post 2018-06-Rapperswil Mailing

1.2. P0009r6 : Pre 2018-06-Rapperswil Mailing

P0009r5 was not taken up at 2018-03-Jacksonville meeting. Related LEWG review of P0900 at 2018-03-Jacksonville meeting

LEWG Poll: We want the ability to customize the access to elements of span (ability to restrict, etc):

span<T, N, Accessor=...>
SF F N A SA
1 1 1 2 8

LEWG Poll: We want the customization of basic_mdspan to be two concepts Mapper and Accessor (akin to Allocator design).

basic_mdspan<T, Extents, Mapper, Accessor>
mdspan<T, N...>
SF F N A SA
3 4 5 1 0

LEWG Poll: We want the customization of basic_mdspan to be an arbitrary (and potentially user-extensible) list of properties.

basic_mdspan<T, Extents, Properties...>
SF F N A SA
1 2 2 6 2

Changes from P0009r5 due to related LEWG reviews:

1.3. P0009r5 : Pre 2018-03-Jacksonville Mailing

LEWG review of P0009r4 at 2017-11-Albuquerque meeting

LEWG Poll: We should be able to index with span<int type[N]> (in addition to array).

SF F N A SA
2 11 1 1 0

Against comment - there is not a proven needs for this feature.

LEWG Poll: We should be able to index with 1d mdspan.

SF F N A SA
0 8 7 0 0

LEWG Poll: We should put the requirement on "rank() <= N" back to "rank()==N".

Unanimous consent

LEWG Poll: With the editorial changes from small group, plus the above polls, forward this to LWG for Fundamentals v3.

Unanimous consent

Changes from P0009r4:

1.4. P0009r4 : Pre 2017-11-Albuquerque Mailing

LEWG review at 2017-03-Kona meeting

LEWG review of P0546r1 at 2017-03-Kona meeting

LEWG Poll: Should we have a single template that covers both single and multi-dimensional spans?

SF F N A SA
1 6 2 6 3

Changes from P0009r3:

1.5. P0009r3 : Post 2016-06-Oulu Mailing

LEWG review at 2016-06-Oulu

LEWG did not like the name array_ref, and suggested the following alternatives: - sci_span - numeric_span - multidimensional_span - multidim_span - mdspan - md_span - vla_span - multispan - multi_span

LEWG Poll: Are member begin()/end() still good?

SF F N A SA
0 2 4 3 1
LEWG Poll: Want this proposal to provide range-producing functions outside array_ref?
SF F N A SA
0 1 3 2 3

LEWG Poll: Want a separate proposal to explore iteration design space?

SF F N A SA
9 1 0 0 0

Changes from P0009r2:

1.6. P0009r2 : Pre 2016-06-Oulu Mailing

LEWG review at 2016-02-Jacksonville.

Changes from P0009r1:

1.7. P0009r1 : Pre 2016-02-Jacksonville Mailing

LEWG review at 2015-10-Kona.

LEWG Poll: What should this feature be called?

Name #
view 5
span 9
array_ref 6
slice 6
array_view 6
ref 0
array_span 7
basic_span 1
object_span 3
field 0

LEWG Poll: Do we want 0-length static extents?

SF F N A SA
3 4 2 3 0

LEWG POLL: Do we want the language to support syntaxes like X[3][][][5]?

Syntax #
view<int[3][0][][5], property1> 12
view<int, dimension<3, 0, dynamic_extent, 5>, property1> 4
view<int[3][0][dynamic_extent][5], property1> 5
view<int, 3, 0, dynamic_extent, 5, property1> 4
view<int, 3, 0, dynamic_extent, 5, properties<property1>> 2
view<arr<int, 3, 0, dynamic_extent, 5>, property1> 4
view<int[3][0][][5], properties<property1>> 9

LEWG POLL: Do we want the variadic property list in template args (either raw or in properties<>)? Note there is no precedence for this in the library.

SF F N A SA
3 6 3 0 0

LEWG POLL: Do we want the per-view bounds-checking knob?

SF F N A SA
3 4 1 2 1

Changes from P0009r0:

1.8. P0009r0 : Pre 2015-10-Kona Mailing

Original non-owning multidimensional array reference (view) paper with motivation, specification, and examples.

Related LEWG review of P0546r1 at 2017-11-Albuquerque meeting

LEWG Poll: span should specify the dynamic extent as the element type of the first template parameter rather than the (current) second template parameter

SF F N A SA
5 3 2 2 0

LEWG Poll: span should support the addition of access properties variadic template parameters

SF F N A SA
0 10 1 5 0

Authors agreed to bring a separate paper ([P0900r0]) discussing how the variadic properties will work.

2. Description

The proposed polymorphic multidimensional array reference (mdspan) defines types and functions for mapping indices from the domain, a multidimensional index space, to the codomain, elements of a contiguous span of objects. A multidimensional index space is defined as the Cartesian product of integer extents, [0..N0) * [0..N1) * [0..N2).... An mdspan has two policies: the layout mapping and the accessor. The layout mapping specifies the formula, and properties of the formula, for mapping a multi-index from the domain to an element in the codomain. The accessor is an extension point that allows modification of how elements are accessed. For example, the Accessors paper (P0367) proposed a rich set of potential access properties.

A multidimensional array is not an array-of-array-of-array-of-array...

The multidimensional array abstraction has been fundamental to numerical computations for over five decades. However, the C/C++ language provides only a one-dimensional array abstraction which can be composed into array-of-array-of-array... types. While such types have some similarity to multidimensional arrays, they do not provide adequate multidimensional array functionality of this proposal. Two critical functionality differences are (1) multiple dynamic extents and (2) polymorphic mapping of multi-indices to element objects.

Optimized Implementation of Layout Mapping

The layout mapping of a multi-index is intended to be an O(1) constexpr operation that is trivially inlined and optimized. Note that Fortran compilers' optimizations include loop invariant code motion, including partial evaluation of multi-index layout mappings when indices are loop-invariant.

3. Editing Notes

The proposed changes are relative to the working draft of the standard as of N4750.

The � character is used to denote a placeholder section number, table number, or paragraph number which the editor shall determine.

Add the header <mdspan> to table 16 in [headers].

Add the header <mdspan> to Table 76 in 26.1 [containers.general] below the listing for <span>.

4. Wording

Text in blockquotes is not proposed wording

The � character is used to denote a placeholder section number which the editor shall determine.

Copy [views] subclause (currently 26.7 in n4750) which contains the wording for span and dynamic_extent which is needed for basic_mdspan


Add the following paragraphs to *[views.general]:

�. The header <mdspan> defines the view basic_mdspan, the type alias mdspan, and other facilities for interacting with these views. The basic_mdspan class template maps a multi-index within a multi-index domain to a reference an element in the codomain span.

�. The subspan function generates a basic_mdspan with a domain contained within the input basic_mdspan domain and codomain contained within the input basic_mdspan codomain.



Add the following subclauses to the end of the [views] subclause (currently 26.7 in n4750):


26.7.� Header <mdspan> synopsis [mdspan.syn]

namespace std {
namespace experimental {
namespace fundamentals_v3 {
  // [mdspan.extents], class template extents
  template<ptrdiff_t... StaticExtents>
    class extents;

  // [mdspan.layout], Layout mapping policies
  class layout_left;
  class layout_right;
  class layout_stride;

  // [mdspan.accessor.basic], class template accessor_basic
  template<class ElementType>
  class accessor_basic;

  // [mdspan.basic], class template mdspan
  template<class ElementType,
           class Extents,
           class LayoutPolicy = layout_right,
           class Accessor = accessor_basic<ElementType>>
    class basic_mdspan;

  template<class T, ptrdiff_t... Extents>
    using mdspan = basic_mdspan<T, extents<Extents...>>;

  // [mdspan.extents.compare], extents comparison operators
  template<ptrdiff_t... LHS, ptrdiff_t... RHS>
    constexpr bool operator==(const extents<LHS...>& lhs, const extents<RHS...>& rhs) noexcept;
  template<ptrdiff_t... LHS, ptrdiff_t... RHS>
    constexpr bool operator!=(const extents<LHS...>& lhs, const extents<RHS...>& rhs) noexcept;

  // [mdspan.subspan], subspan creation
  template<class ElementType, class Extents, class LayoutPolicy,
           class Accessor, class... SliceSpecifiers>
    basic_mdspan<ElementType, /* see-below */>
      subspan(const basic_mdspan<ElementType, Extents, LayoutPolicy, Accessor>&, SliceSpecifiers...) noexcept;

  // tag supporting subspan
  struct all_type { explicit all_type() = default; };
  inline constexpr all_type all = all_type{};
}}}

26.7.� Class template extents [mdspan.extents]

26.7.�.1 Overview [mdspan.extents.overview]

  1. An extents object defines a multidimensional index space which is the Cartesian product of integers extents [0..N0) * [0..N1) *....

  2. The dynamic extents of an extents object correspond to the StaticExtents template parameters that are equal to dynamic_extent. Let DynamicRank[i] denote the index of the ith such extent in the StaticExtents template parameter pack, and let DynamicIndex[r] indicate the number of such extents in the first r entries of the StaticExtents parameter pack

  3. An extents object is expected to store dynamic extents. [Note: An implementation should not consume storage for static extents. — end note]

  4. If any of StaticExtents are negative and not equal to dynamic_extent, the program is ill-formed.

namespace std {
namespace experimental {
namespace fundamentals_v3 {

template<ptrdiff_t... StaticExtents>
class extents {
public:
  // types
  using index_type = ptrdiff_t;

  // [mdspan.extents.cons], Constructors and assignment
  constexpr extents() noexcept;
  constexpr extents(const extents&) noexcept;
  constexpr extents(extents&&) noexcept;
  template<class... IndexType>
  constexpr extents(IndexType... dynamic_extents) noexcept;
  template<class IndexType, size_t rank_dynamic>
  constexpr extents(const array<IndexType, rank_dynamic>&) noexcept;
  template<ptrdiff_t... OtherStaticExtents>
  constexpr extents(const extents<OtherStaticExtents...>& other);
  ~extents() = default;

  constexpr extents& operator=(const extents&) noexcept = default;
  constexpr extents& operator=(extents&&) noexcept = default;
  template<ptrdiff_t... OtherStaticExtents>
  constexpr extents& operator=(const extents<OtherStaticExtents...>& other);

  // [mdspan.extents.obs], Observers of the domain multi-index space
  static constexpr size_t rank() noexcept;
  static constexpr size_t rank_dynamic() noexcept;
  static constexpr index_type static_extent(size_t) noexcept;
  constexpr index_type extent(size_t) const noexcept;

private:
  array<index_type, rank_dynamic()> dynamic_extents_; // exposition only
};

}}}

26.7.�.2 Constructors and assignment [mdspan.extents.cons]

constexpr extents() noexcept;


constexpr extents(const extents& other);
constexpr extents(extents&& other);


template<ptrdiff_t... OtherStaticExtents>
constexpr extents(const extents<OtherStaticExtents...>& other);


template<class... IndexType>
constexpr extents(IndexType... dynamic_extents) noexcept;
template<class IndexType, size_t rank_dynamic>
constexpr extents(const array<IndexType, rank_dynamic> & dynamic_extents) noexcept;
template<ptrdiff_t... OtherStaticExtents>
constexpr extents& operator=(const extents<OtherStaticExtents...>& other);



26.7.�.3 Observers of the domain multi-index space [mdspan.extents.obs]


static constexpr size_t rank() const noexcept;


static constexpr size_t rank_dynamic() const noexcept;


static constexpr index_type static_extent(size_t r) const noexcept;


constexpr index_type extent(size_t r) const noexcept;


26.7.�.4 extents comparison operators [mdspan.extents.compare]

template<ptrdiff_t... LHS, ptrdiff_t... RHS>
  constexpr bool operator==(const extents<LHS...>& lhs, const extents<RHS...>& rhs) noexcept;


template<ptrdiff_t... LHS, ptrdiff_t... RHS>
  constexpr bool operator!=(const extents<LHS...>& lhs, const extents<RHS...>& rhs) noexcept;



26.7.� Layout mapping policy [mdspan.layout]

26.7.�.1 Layout mapping requirements [mdspan.layout.reqs]

  1. A layout mapping policy is a class that contains a layout mapping, a nested class template.

  2. A layout mapping policy shall meet the requirements in table �.

  3. A layout mapping shall meet the requirements of DefaultConstructible, CopyAssignable, EqualityComparable, and the requirements in table �.

  4. In Table �:

    • MP denotes a layout mapping policy.

    • E denotes a specialization of extents.

    • e denotes an object of type E defining a domain multi-index space.

    • r is a value of an integral type such that 0 <= r < e.rank().

    • i... and j... are packs of an integer type denoting values in the multi-index space e, the r*th member of packs i... and j... are denoted by i[r] and j[r], and sizeof...(i)==E::rank(), 0 <= i[r] < e.extent(r), sizeof...(j)==E::rank(), and 0 <= j[r] < e.extent(r).

    • M denotes a layout mapping class.

    • m denotes an object of type M that maps a multi-index i... to an integral value.

Table � — Layout mapping policy and layout mapping requirements

Expression Return Type Operational Semantics Requires/Remarks
MP::template mapping<E> M
m.get_extents() E

Returns: e.

m(i...) E::index_type Returns: Mapping of a multi-index i... Requires: 0 <= m(i...)
m.required_span_size() E::index_type Returns: one plus the maximum value of m(i...).
m.is_unique() bool Returns: true if m(i...)!=m(j...) for every i...!=j...
m.is_contiguous() bool Returns: true if the set of values defined by m(i)... is equal to the set of values consisting of 0...m.required_span_size()-1
m.is_strided() bool Returns: true if m(j...) - m(i...) = s[r] when all members of j... and i... are equal except for exactly one rth member such that j[r]==i[r]+1. s[r] is the stride of ordinate r.
M::is_always_unique() bool Returns: true if m.is_unique()==true for any object of type M.
M::is_always_contiguous() bool Returns: true if m.is_contiguous()==true for any object of type M.
M::is_always_strided() bool Returns: true if m.is_strided()==true for any object of type M.
m.stride(r) E::index_type Returns: m(j...) - m(i...) when all members of j... and i... are equal except for the rth member such that j[r]==i[r]+1. Requires: m.is_strided()==true



26.7.�.2 Class layout_left [mdspan.layout.left]

  1. layout_left meets the requirements of layout mapping policy. [Note: Thus, any well-formed specialization of layout_left::template mapping meets the requirements of layout mapping —end note]

  2. layout_left gives a layout mapping where the left-most extent is stride one and strides increase left-to-right as the product of extents.

  3. If Extents is not a (possibly cv-qualified) specialization of extents, the program is ill-formed.

namespace std {
namespace experimental {
namespace fundamentals_v3 {

struct layout_left {
  template<class Extents>
  class mapping {
  public:
    // [mdspan.layout.left.cons], layout_left::mapping constructors
    constexpr mapping() noexcept;
    constexpr mapping(const mapping& other) noexcept;
    constexpr mapping(mapping&& other) noexcept;
    constexpr mapping(const Extents& e) noexcept;
    template<class OtherExtents>
      constexpr mapping(const mapping<OtherExtents>& other);

    mapping& operator=() noexcept = default;
    mapping& operator=(const mapping& other) noexcept = default;
    template<class OtherExtents>
      constexpr mapping& operator=(const mapping<OtherExtents>& other);

    // [mdspan.layout.left.ops], layout_left::mapping operations
    Extents get_extents() const noexcept;

    constexpr typename Extents::index_type required_span_size() const noexcept;

    template<class... Indices>
      typename Extents::index_type operator()(Indices... is) const;

    static constexpr bool is_always_unique();
    static constexpr bool is_always_contiguous();
    static constexpr bool is_always_strided();

    constexpr bool is_unique() const;
    constexpr bool is_contiguous() const;
    constexpr bool is_strided() const;

    template<class OtherExtents>
      constexpr bool operator==(const mapping<OtherExtents>& other) const noexcept;
    template<class OtherExtents>
      constexpr bool operator!=(const mapping<OtherExtents>& other) const noexcept;

    typename Extents::index_type stride(size_t rank) const noexcept;

  private:
    Extents extents_; // exposition only
  };
};

}}}


26.7.�.2.1 layout_left::mapping constructors [mdspan.layout.left.cons]


constexpr mapping() noexcept;


constexpr mapping(const mapping& other) noexcept;


constexpr mapping(mapping&& other) noexcept;


constexpr mapping(const Extents & e) noexcept;


template<class OtherExtents>
constexpr mapping(const mapping<OtherExtents>& other);



26.7.�.2.2 layout_left::mapping operations [mdspan.layout.left.ops]


Extents get_extents() const noexcept;


typename Extents::index_type required_span_size() const noexcept;


template<class... Indices>
  typename Extents::index_type operator()(Indices... i) const;

Let i[k] denote the kth member of i....

Extents::index_type offset = 0 ;
for(size_t k=0; k<get_extents.rank(); ++k) 
  offset += i[k]*stride(k);

   


static constexpr bool is_always_unique();
static constexpr bool is_always_contiguous();
static constexpr bool is_always_strided();
constexpr bool is_unique() const;
constexpr bool is_contiguous() const;
constexpr bool is_strided() const;


typename Extents::index_type stride(size_t r) const
Extents::index_type s = 1;
for(size_t k=0; k<r; ++k)
  s *= get_extents().extent(k);


template<class OtherExtents>
  constexpr bool operator==(const mapping<OtherExtents>& other) const noexcept;


template<class OtherExtents>
  constexpr bool operator!=(const mapping<OtherExtents>& other) const noexcept;



26.7.�.3 Class layout_right [mdspan.layout.right]

  1. layout_right meets the requirements of layout mapping policy. [Note: Thus, any well-formed specialization of layout_right::template mapping meets the requirements of layout mapping —end note]

  2. The layout mapping property layout_right gives a layout mapping where the right-most extent is stride one and strides increase right-to-left as the product of extents.

  3. If Extents is not a (possibly cv-qualified) specialization of extents, the program is ill-formed.

namespace std {
namespace experimental {
namespace fundamentals_v3 {

struct layout_right {
  template<class Extents>
  class mapping {
  public:
    // [mdspan.layout.right.cons], layout_left::mapping constructors
    constexpr mapping() noexcept;
    constexpr mapping(const mapping& other) noexcept;
    constexpr mapping(mapping&& other) noexcept;
    constexpr mapping(Extents e) noexcept;
    template<class OtherExtents>
      constexpr mapping(const mapping<OtherExtents>& other);

    mapping& operator=() noexcept = default;
    mapping& operator=(const mapping& other) noexcept = default;
    template<class OtherExtents>
      constexpr mapping& operator=(const mapping<OtherExtents>& other);

    // [mdspan.layout.right.ops], layout_right::mapping operations
    Extents get_extents() const noexcept;

    constexpr typename Extents::index_type required_span_size() const noexcept;

    template<class... Indices>
      typename Extents::index_type operator()(Indices... is) const;

    static constexpr bool is_always_unique() noexcept;
    static constexpr bool is_always_contiguous() noexcept;
    static constexpr bool is_always_strided() noexcept;

    constexpr bool is_unique() const noexcept;
    constexpr bool is_contiguous() const noexcept;
    constexpr bool is_strided() const noexcept;

    typename Extents::index_type stride(size_t rank) const noexcept;

    template<class OtherExtents>
      constexpr bool operator==(const mapping<OtherExtents>& other) const noexcept;
    template<class OtherExtents>
      constexpr bool operator!=(const mapping<OtherExtents>& other) const noexcept;

  private:
    Extents extents_; // exposition only
  };
};
}


26.7.�.3.1 layout_right::mapping constructors [mdspan.layout.right.cons]


constexpr mapping() noexcept;


constexpr mapping(const mapping& other) noexcept;


constexpr mapping(mapping&& other) noexcept;


constexpr mapping(Extents e) noexcept;


template<class OtherExtents>
  constexpr mapping(const mapping<OtherExtents>& other);



26.7.�.3.2 layout_right::mapping operations [mdspan.layout.right.ops]


Extents get_extents() const noexcept;


typename Extents::index_type required_span_size() const noexcept;


template<class... Indices>
  typename Extents::index_type operator()(Indices... i) const noexcept;

Let i[k] denote the kth member of i....

index_type offset = 0;
for(size_t k=0; k<Extents::rank(); ++k) 
  offset += i[k]*stride(k);


static constexpr bool is_always_unique() noexcept;
static constexpr bool is_always_contiguous() noexcept;
static constexpr bool is_always_strided() noexcept;
constexpr bool is_unique() const noexcept;
constexpr bool is_contiguous() const noexcept;
constexpr bool is_strided() const noexcept;


typename Extents::index_type stride(size_t r) const noexcept;
Extents::index_type s = 1;
for(size_t k=r+1; k<get_extents.rank(); ++k)
  s *= get_extents(k);


template<class OtherExtents>
  constexpr bool operator==(const mapping<OtherExtents>& other) const noexcept;


template<class OtherExtents>
  constexpr bool operator!=(const mapping<OtherExtents>& other) const noexcept;



26.7.�.4 Class layout_stride [mdspan.layout.stride]

  1. layout_stride meets the requirements of layout mapping policy.

  2. The layout mapping property layout_stride gives a layout mapping where the strides are user defined.

  3. If Extents is not a (possibly cv-qualified) specialization of extents, the program is ill-formed.


namespace std {
namespace experimental {
namespace fundamentals_v3 {

struct layout_stride {
  template<class Extents>
  class mapping {
  public:
    // [mdspan.layout.stride.cons], layout_stride::mapping constructors
    constexpr mapping() noexcept;
    constexpr mapping(mapping const& other) noexcept;
    constexpr mapping(mapping&& other) noexcept;
    constexpr mapping(Extents e, array<typename Extents::index_type, Extents::rank()> s) noexcept;
    template<class OtherExtents>
      constexpr mapping(const mapping<OtherExtents>& other);

    mapping& operator=() noexcept = default;
    mapping& operator=(const mapping& other) noexcept = default;
    template<class OtherExtents>
      constexpr mapping& operator=(const mapping<OtherExtents>& other);

    // [mdspan.layout.stride.ops], layout_stride::mapping operations
    Extents get_extents() const noexcept;
    array<typename Extents::index_type, Extents::rank()> get_strides() const noexcept;

    constexpr typename Extents::index_type required_span_size() const noexcept;

    template<class... Indices>
      typename Extents::index_type operator()(Indices... is) const;

    static constexpr bool is_always_unique() noexcept;
    static constexpr bool is_always_contiguous() noexcept;
    static constexpr bool is_always_strided() noexcept;

    constexpr bool is_unique() const noexcept;
    constexpr bool is_contiguous() const noexcept;
    constexpr bool is_strided() const noexcept;

    typename Extents::index_type stride(size_t rank) const noexcept;

    template<class OtherExtents>
      constexpr bool operator==(const mapping<OtherExtents>& other) const noexcept;
    template<class OtherExtents>
      constexpr bool operator!=(const mapping<OtherExtents>& other) const noexcept;

  private:
    Extents extents_; // exposition only
    array<typename Extents::index_type, Extents::rank()> strides_; // exposition only
  };
};
}}}


26.7.�.4.1 layout_stride::mapping constructors [mdspan.layout.stride.cons]

constexpr mapping() noexcept;
constexpr mapping(const mapping& other) noexcept;
constexpr mapping(mapping&& other) noexcept;
constexpr mapping(Extents e, array<typename Extents::index_type, Extents::rank()> s) noexcept;
template<class OtherExtents>
  constexpr mapping(const mapping<OtherExtents>& other);

26.7.�.4.2 layout_stride::mapping operations [mdspan.layout.stride.ops]

Extents get_extents() const noexcept;
array<typename Extents::index_type, Extents::rank()> get_strides() const noexcept;
typename Extents::index_type required_span_size() const noexcept;
template <class... Indices>
  typename Extents::index_type operator()(Indices... i) const noexcept;
static constexpr bool is_always_unique() noexcept;
static constexpr bool is_always_strided() noexcept;
constexpr bool is_unique() const noexcept;
constexpr bool is_strided() const noexcept;
static constexpr bool is_always_contiguous() noexcept;
constexpr bool is_contiguous() const noexcept;
typename Extents::index_type stride(size_t r) const noexcept;


template<class OtherExtents>
  constexpr bool operator==(const mapping<OtherExtents>& other) const noexcept;


template<class OtherExtents>
  constexpr bool operator!=(const mapping<OtherExtents>& other) const noexcept;



26.7.� Accessor [mdspan.accessor]


26.7.�.1 Accessor requirements [mdspan.accessor.reqs]

  1. An accessor is a class that converts a pointer and an offset into a reference. [Note: The intended semantic is that the reference refers to a value at the given offset from the given pointer. —end note]

  2. An accessor shall meet the requirements of DefaultConstructible, CopyAssignable, and the requirements in table �.

In Table �:

Table �: Accessor requirements

Expression Return Type Requirements/Notes
A::value_type
A::pointer Requires: A::pointer shall meet the requirements of random access iterator ([random.access.iterators]), and iterator_traits<A::pointer>::value_type shall be exactly A::value_type.
A::reference Requires: iterator_traits<A::pointer>::reference shall be convertible to A::reference
a(p, i) A::reference [Note: basic_mdspan implementations behave as if they use this expression in place of p[i]. - end note]


26.7.�.2 Class accessor_basic [mdspan.accessor.basic]

  1. accessor_basic meets the requirements of accessor.

  2. accessor_basic gives an accessor that has semantics equivalent to dereferencing a pointer to an array of values.

  3. If T is not an object type or is an array type, the program is ill-formed.

namespace std {
namespace experimental {
namespace fundamentals_v3 {

template<class T>
struct accessor_basic {
  using value_type = T;
  using pointer = T*;
  using reference = T&;

  // [mdspan.accessor.basic.ops], <code data-opaque bs-autolink-syntax='`accessor_basic`'>accessor_basic</code> operations
  constexpr reference operator()(pointer p, ptrdiff_t i) const noexcept;
};

}}}


26.7.�.2.1 accessor_basic operations [mdspan.accessor.basic.ops]

constexpr reference operator()(pointer p, ptrdiff_t i) const noexcept;



26.7.� Class template basic_mdspan [mdspan.basic]

  1. The basic_mdspan class template maps a multi-index within a multi-index domain to a reference to an element in the codomain span.

  2. The multi-index domain space is the Cartesian product of the extents: [0, extent(0)) * [0, extent(1)) * ... * [0, extent(rank() - 1)). Each extent may be statically or dynamically specified.

  3. As with span, the storage of the objects in the codomain span of a basic_mdspan is owned by some other object.

  4. ElementType is required to be a complete object type that is not an abstract class type or an array type.

  5. Extents is required to be a (cv-unqualified) specialization of extents.

  6. LayoutPolicy is required to be a cv-unqualified object type.

  7. If LayoutPolicy does not meet the layout mapping policy requirements, the program is ill-formed.

  8. Accessor is required to be a cv-unqualified object type.

  9. If Accessor does not meet the accessor requirements, or if Accessor::value_type is not exactly ElementType, the program is ill-formed.

namespace std {
namespace experimental {
namespace fundamentals_v3 {

template<class ElementType, class Extents, class LayoutPolicy, class Accessor>
class basic_mdspan {
public:

  // Domain and codomain types
  using layout = LayoutPolicy;
  using mapping = typename layout::template mapping<Extents>;
  using accessor = Accessor;
  using element_type = typename accessor::value_type;
  using value_type = remove_cv_t<element_type>;
  using index_type = ptrdiff_t ;
  using difference_type = ptrdiff_t ;
  using pointer = typename accessor::pointer;
  using reference = typename accessor::reference;

  // [mdspan.basic.cons], basic_mdspan constructors, assignment, and destructor
  constexpr basic_mdspan() noexcept = default;
  constexpr basic_mdspan(const basic_mdspan&) noexcept = default;
  constexpr basic_mdspan(basic_mdspan&&) noexcept;
  template<class... IndexType>
    explicit constexpr basic_mdspan(pointer p, IndexType... dynamic_extents);
  template<class... IndexType>
    explicit constexpr basic_mdspan(const span<element_type>& sp, IndexType... dynamic_extents);
  template<class IndexType, size_t N>
    explicit constexpr basic_mdspan(pointer p, const array<IndexType, N>& dynamic_extents);
  template<class IndexType, size_t N>
    explicit constexpr basic_mdspan(const span<element_type>& sp, const array<IndexType, N>& dynamic_extents);
  constexpr basic_mdspan(pointer p, const mapping& m);
  constexpr basic_mdspan(const span<element_type>& sp, const mapping& m);
  constexpr basic_mdspan(pointer p, const mapping& m, const accessor& a);
  constexpr basic_mdspan(const span<element_type>& sp, const mapping& m, const accessor& a);
  template<class OtherElementType, class OtherExtents, class OtherLayoutPolicy, class OtherAccessor>
    constexpr basic_mdspan(const basic_mdspan<OtherElementType, OtherExtents, OtherLayoutPolicy, OtherAccessor>& other);

  ~basic_mdspan() = default;

  constexpr basic_mdspan& operator=(const basic_mdspan&) noexcept = default;
  constexpr basic_mdspan& operator=(basic_mdspan&&) noexcept = default;
  template<class OtherElementType, class OtherExtents, class OtherLayoutPolicy, class OtherAccessor>
    constexpr basic_mdspan& operator=(const basic_mdspan<OtherElementType, OtherExtents, OtherLayoutPolicy, OtherAccessor>& other) noexcept;

  // [mdspan.basic.mapping], basic_mdspan mapping domain multi-index to access codomain element
  constexpr reference operator[](index_type) const noexcept;
  template<class... IndexType>
    constexpr reference operator()(IndexType... indices) const noexcept;
  template<class IndexType, size_t N>
    constexpr reference operator()(const array<IndexType, N>& indices) const noexcept;
  accessor get_accessor() const;

  // [mdspan.basic.domobs], basic_mdspan observers of the domain multi-index space
  static constexpr int rank() noexcept;
  static constexpr int rank_dynamic() noexcept;
  static constexpr index_type static_extent(size_t) noexcept;

  constexpr Extents get_extents() const noexcept;
  constexpr index_type extent(size_t) const noexcept;
  constexpr index_type size() const noexcept;
  constexpr index_type unique_size() const noexcept;

  // [mdspan.basic.codomain], basic_mdspan observers of the codomain
  constexpr span<element_type> span() const noexcept;

  // [mdspan.basic.obs], basic_mdspan observers of the mapping
  static constexpr bool is_always_unique() noexcept;
  static constexpr bool is_always_contiguous() noexcept;
  static constexpr bool is_always_strided() noexcept;

  constexpr mapping get_mapping() const noexcept;
  constexpr bool is_unique() const noexcept;
  constexpr bool is_contiguous() const noexcept;
  constexpr bool is_strided() const noexcept;
  constexpr index_type stride(size_t) const;

private:
  accessor acc_; // exposition only
  mapping map_; // exposition only
  pointer ptr_; // exposition only
};

}}}


26.7.�.1 basic_mdspan constructors, assignment, and destructor [mdspan.basic.cons] // Mapping domain multi-index to access codomain element

constexpr basic_mdspan() noexcept = default;


constexpr basic_mdspan(const basic_mdspan& other) noexcept = default;


constexpr basic_mdspan(basic_mdspan&& other) noexcept;


template<class... IndexType>
  explicit constexpr basic_mdspan(pointer ptr, IndexType... dynamic_extents);


template<class... IndexType>
  explicit constexpr basic_mdspan(const span<element_type>& sp, IndexType... dynamic_extents);


template<class IndexType, size_t N>
  explicit constexpr basic_mdspan(pointer p, const array<IndexType, N>& dynamic_extents);


template<class IndexType, size_t N>
  explicit constexpr basic_mdspan(const span<element_type>& sp, const array<IndexType, N>& dynamic_extents);


constexpr basic_mdspan(pointer p, const mapping& m);


constexpr basic_mdspan(const span<element_type>& sp, const mapping& m);


constexpr basic_mdspan(pointer p, const mapping& m, const accessor& a);


constexpr basic_mdspan(const span<element_type>& sp, const mapping& m, const accessor& a);


template<class OtherElementType, class OtherExtents, class OtherLayoutPolicy, class OtherAccessor>
  constexpr basic_mdspan(const basic_mdspan<OtherElementType, OtherExtents, OtherLayoutPolicy, OtherAccessor>& other);


template<class OtherElementType, class OtherExtents, class OtherLayoutPolicy, class OtherAccessor>
  constexpr basic_mdspan& operator=(const basic_mdspan<OtherElementType, OtherExtents, OtherLayoutPolicy, OtherAccessor>& other);


26.7.�.2 basic_mdspan mapping domain multi-index to access codomain element [mdspan.basic.mapping]

constexpr reference operator[](index_type i) const;


template<class... IndexType>
  constexpr reference operator()(IndexType... indices) const;


template<class IndexType, size_t N>
  constexpr reference operator()(const array<IndexType, N>& indices) const;
accessor get_accessor() const;


26.7.�.3 basic_mdspan observers of the domain multi-index space [mdspan.basic.domobs]

static constexpr int rank() noexcept;


static constexpr int rank_dynamic() noexcept;


static constexpr index_type static_extent(size_t r) noexcept;


constexpr Extents get_extents() const noexcept;


constexpr index_type extent(size_t r) const noexcept;


constexpr index_type size() const noexcept;
constexpr index_type unique_size() const noexcept;


26.7.�.3 basic_mdspan observers of the codomain [mdspan.basic.codomain]

constexpr span<element_type> span() const noexcept;


26.7.�.4 basic_mdspan observers of the mapping [mdspan.basic.obs]


static constexpr bool is_always_unique() noexcept;


static constexpr bool is_always_contiguous() noexcept;


static constexpr bool is_always_strided() noexcept;


constexpr mapping get_mapping() const noexcept;


constexpr bool is_unique() const noexcept;


constexpr bool is_contiguous() const noexcept;


constexpr bool is_strided() const noexcept;


constexpr index_type stride(size_t r) const;



26.7.� subspan [mdspan.subspan]

namespace std {
namespace experimental {
namespace fundamentals_v3 {

  // [mdspan.subspan], subspan creation
  template<class ElementType, class Extents, class LayoutPolicy,
           class Accessor, class... SliceSpecifiers>
    basic_mdspan<ElementType, E /* see-below */, layout_stride, Accessor>
      subspan(const basic_mdspan<ElementType, Extents, LayoutPolicy, Accessor>& src, SliceSpecifiers ... slices) noexcept;

}}}

subspan creates a basic_mdspan that is a view on a (potentially trivial) subset of another basic_mdspan. The SliceSpecifier parameters indicate the subset that the return value references. Let slices[r] denote the rth value in the parameter pack slices. The second template parameter of the return type is E, a specialization of extents.

Let C be the number of parameters in SliceSpecifiers which are convertible to ptrdiff_t
Let ranges[r] denote the rth value in the parameter pack slices, which is not convertible to ptrdiff_t
Let sub be the return value of subspan
Let first[r] denote the rth lower bound of slices[r].

Let last[r] denote the rth upper bound of slices[r].

Requires:

Postcondition:

// Create a mapping
typedef extents<3,dynamic_extent,7> Extents3D;
layout_right::template mapping<Extents3D> map_right(10);

// Allocate a basic_mdspan
int* ptr = new int[3*8*10];
basic_mdspan<int,Extents3D,layout_right> a(ptr,map_right);

// Initialize the span
for(int i0=0; i0<a.extent(0); i0++)
  for(int i1=0; i1<a.extent(1); i1++)
    for(int i2=0; i2<a.extent(2); i2++)
      a(i0,i1,i2) = 10000*i0+100*i1+i2;

// Create Subspan
auto a_sub = subspan(a,1,std::pair<int,int>(4,6),std::pair<int,int>(1,6));

// Print values of subspan
for(int i0=0; i0<a_sub.extent(0); i0++) {
  for(int i1=0; i1<a_sub.extent(1); i1++)
    std::cout << a_sub(i0,i1) << " ";
  std::cout << std::endl;
}

/* Output
10401 10402 10403 10404 10405
10501 10502 10503 10504 10505
*/

5. Next Steps

LEWG issue

Previous paper:

P0860 : Access Policy Generating Proxy Reference

The reference type may be a proxy for accessing an element_type object. For example, the atomic AccessorPolicy in P0860 defines AccessorPolicy::template accessor<T>::reference to be atomic_ref<T> from P0019.

Related papers:

7. TODO

References

Informative References

[N4355]
Carter Edwards. Shared Multidimensional Arrays with Polymorphic Layout. 4 February 2015. URL: https://wg21.link/n4355
[P0900r0]
David S. Hollman. An Ontology for Properties of mdspan. 12 February 2018. URL: https://wg21.link/p0900r0