23 Containers library [containers]

23.7 Views [views]

23.7.3 Multidimensional access [views.multidim]

23.7.3.7 submdspan [mdspan.sub]


23.7.3.7.1 Overview [mdspan.sub.overview]

23.7.3.7.2 strided_slice [mdspan.sub.strided.slice]

23.7.3.7.3 submdspan_mapping_result [mdspan.sub.map.result]

23.7.3.7.4 Exposition-only helpers [mdspan.sub.helpers]

23.7.3.7.5 submdspan slice canonicalization [mdspan.sub.canonical]

23.7.3.7.6 submdspan_extents function [mdspan.sub.extents]

23.7.3.7.7 Specializations of submdspan_mapping [mdspan.sub.map]

23.7.3.7.7.1 Sliceable layout mapping requirements [mdspan.sub.map.sliceable]

23.7.3.7.7.2 Common [mdspan.sub.map.common]

23.7.3.7.7.3 layout_left specialization of submdspan_mapping [mdspan.sub.map.left]

23.7.3.7.7.4 layout_right specialization of submdspan_mapping [mdspan.sub.map.right]

23.7.3.7.7.5 layout_stride specialization of submdspan_mapping [mdspan.sub.map.stride]

23.7.3.7.7.6 layout_left_padded specialization of submdspan_mapping [mdspan.sub.map.leftpad]

23.7.3.7.7.7 layout_right_padded specialization of submdspan_mapping [mdspan.sub.map.rightpad]

23.7.3.7.8 submdspan function template [mdspan.sub.sub]


23.7.3.7.1 Overview [mdspan.sub.overview]

The submdspan facilities create a new mdspan viewing a subset of elements of an existing input mdspan.
The subset viewed by the created mdspan is determined by the SliceSpecifier arguments.
Given a signed or unsigned integer type IndexType, a type S is a submdspan slice type for IndexType if at least one of the following holds:
  • is_convertible_v<S, full_extent_t> is true;
  • is_convertible_v<S, IndexType> is true;
  • S is a specialization of strided_slice and is_convertible_v<X, IndexType> is true for X denoting S​::​offset_type, S​::​extent_type, and S​::​stride_type; or
  • all of the following hold:
    • the declaration auto [...ls] = std​::​move(s); is well-formed for some object s of type S,
    • sizeof...(ls) is equal to 2, and
    • (is_convertible_v<decltype(std​::​move(ls)), IndexType> && ...) is true.
Given a signed or unsigned integer type IndexType, a type S is a canonical submdspan index type for IndexType if S is either IndexType or constant_wrapper<v> for some value v of type IndexType, such that v is greater than or equal to zero.
Given a signed or unsigned integer type IndexType, a type S is a canonical submdspan slice type for IndexType if exactly one of the following is true:
  • S is full_extent_t;
  • S is a canonical submdspan index type for IndexType; or
  • S is a specialization of strided_slice where all of the following hold:
    • S​::​offset_type, S​::​extent_type, and S​::​stride_type are all canonical submdspan index types for IndexType; and
    • if S​::​stride_type and S​::​extent_type are both specializations of constant_wrapper, then S​::​stride_type​::​value is greater than zero.
A type S is a collapsing slice type if it is neither full_extent_t nor a specialization of strided_slice.
[Note 1: 
Each collapsing slice type in submdspan_mapping's parameter pack of slice specifier types reduces the rank of the result of submdspan_mapping by one.
— end note]
A type S is a unit-stride slice type if
  • S is a specialization of strided_slice where S​::​stride_type is a specialization of constant_wrapper and S​::​stride_type​::​value is equal to 1, or
  • S denotes full_extent_t.
Given an object e of type E that is a specialization of extents, and an object s of type S that is a canonical submdspan slice type for E​::​index_type, the submdspan slice range of s for the extent of e is:
  • [0, e.extent(k)), if S is full_extent_t;
  • [E​::​index_type(s.offset), E​::​index_type(s.offset + s.extent)), if S is a specialization of strided_slice; otherwise
  • [E​::​index_type(s), )
Given a type E that is a specialization of extents, a type S is a valid submdspan slice type for the extent of E if S is a canonical slice type for E​::​index_type, and for x equal to E​::​static_extent(k), either x is equal to dynamic_extent; or
  • if S is a specialization of strided_slice:
    • if S​::​offset_type is a specialization of constant_wrapper, then S​::​offset_type​::​value is less than or equal to x;
    • if S​::​offset_type is a specialization of constant_wrapper, then S​::​extent_type​::​value is less than or equal to x; and
    • if both S​::​offset_type and S​::​extent_type are specializations of constant_wrapper, then S​::​offset_type​::​value + S​::​extent_type​::​value is less than or equal to x; or
  • if S is a specialization of constant_wrapper, then S​::​value is less than x.
Given an object e of type E that is a specialization of extents and an object s of type S, s is a valid submdspan slice for the extent of e if
  • S is a valid submdspan slice type for the extent of E;
  • the interval of e contains the submdspan slice range of s for the extent of e; and
  • if S is a specialization of strided_slice, then:
    • s.extent is greater than or equal to zero, and
    • either s.extent equals zero or s.stride is greater than zero.

23.7.3.7.2 strided_slice [mdspan.sub.strided.slice]

strided_slice represents a set of extent regularly spaced integer indices.
The indices start at offset, and increase by increments of stride.
namespace std { template<class OffsetType, class ExtentType, class StrideType> struct strided_slice { using offset_type = OffsetType; using extent_type = ExtentType; using stride_type = StrideType; [[no_unique_address]] offset_type offset{}; [[no_unique_address]] extent_type extent{}; [[no_unique_address]] stride_type stride{}; }; }
strided_slice has the data members and special members specified above.
It has no base classes or members other than those specified.
Mandates: OffsetType, ExtentType, and StrideType are signed or unsigned integer types, or model integral-constant-like.
[Note 1: 
strided_slice{.offset = 1, .extent = 10, .stride = 3} indicates the indices 1, 4, 7, and 10.
Indices are selected from the half-open interval [1, 1 + 10).
— end note]

23.7.3.7.3 submdspan_mapping_result [mdspan.sub.map.result]

Specializations of submdspan_mapping_result are returned by overloads of submdspan_mapping.
namespace std { template<class LayoutMapping> struct submdspan_mapping_result { [[no_unique_address]] LayoutMapping mapping = LayoutMapping(); size_t offset{}; }; }
submdspan_mapping_result has the data members and special members specified above.
It has no base classes or members other than those specified.
LayoutMapping shall meet the layout mapping requirements ([mdspan.layout.policy.reqmts]).

23.7.3.7.4 Exposition-only helpers [mdspan.sub.helpers]

For a pack p and an integer i, let MAP_RANK(p, i) be the number of elements p...[j] for whose types are not collapsing slice types.
template<class T> concept is-strided-slice = see below;
The concept is-strided-slice<T> is satisfied and modeled if and only if T is a specialization of strided_slice.
template<class IndexType, class S> constexpr auto canonical-index(S s);
Mandates: If S models integral-constant-like, then extents<IndexType>​::​index-cast(S​::​value) is representable as a value of type IndexType.
Preconditions: extents<IndexType>​::​index-cast(std​::​move(s)) is representable as a value of type IndexType.
Effects: Equivalent to:
template<class IndexType, class S> constexpr auto canonical-slice(S s);
Mandates: S is a submdspan slice type for IndexType.
Effects: Equivalent to: if constexpr (is_convertible_v<S, full_extent_t>) { return static_cast<full_extent_t>(std::move(s)); } else if constexpr (is_convertible_v<S, IndexType>) { return canonical-index<IndexType>(std::move(s)); } else if constexpr (is-strided-slice<S>) { auto c_extent = canonical-index<IndexType>(std::move(s.extent)); auto c_offset = canonical-index<IndexType>(std::move(s.offset)); if constexpr (is_same_v<decltype(c_extent), constant_wrapper<IndexType(0)>>) { return strided_slice{ .offset = c_offset, .extent = c_extent, .stride = cw<IndexType(1)> }; } else { return strided_slice{ .offset = c_offset, .extent = c_extent, .stride = canonical-index<IndexType>(std::move(s.stride)) }; } } else { auto [s_first, s_last] = std::move(s); auto c_first = canonical-index<IndexType>(std::move(s_first)); auto c_last = canonical-index<IndexType>(std::move(s_last)); return strided_slice{ .offset = c_first, .extent = canonical-index<IndexType>(c_last - c_first), .stride = cw<IndexType(1)> }; }

23.7.3.7.5 submdspan slice canonicalization [mdspan.sub.canonical]

template<class IndexType, size_t... Extents, class... SliceSpecifiers> constexpr auto submdspan_canonicalize_slices(const extents<IndexType, Extents...>& src, SliceSpecifiers... slices);
Constraints: sizeof...(SliceSpecifiers) equals sizeof...(Extents).
Mandates: For each rank index k of src:
  • SliceSpecifiers...[k] is a submdspan slice type for IndexType, and
  • decltype(canonical-slice<IndexType>(slices...[k])) is a valid submdspan slice type for the extent of extents<IndexType, Extents...>.
Preconditions: For each rank index k of src, canonical-slice<IndexType>(slices...[k]) is a valid submdspan slice for the extent of src.
Returns: make_tuple(canonical-slice<IndexType>(slices)...).

23.7.3.7.6 submdspan_extents function [mdspan.sub.extents]

template<class IndexType, size_t... Extents, class... SliceSpecifiers> constexpr auto submdspan_extents(const extents<IndexType, Extents...>& src, SliceSpecifiers... raw_slices);
Let slices be the pack introduced by the following declaration: auto [...slices] = submdspan_canonicalize_slices(src, raw_slices...);
Constraints: sizeof...(SliceSpecifiers) equals sizeof...(Extents).
Mandates: For each rank index k of src:
  • SliceSpecifiers...[k] is a submdspan slice type for IndexType, and
  • decltype(slices...[k]) is a valid submdspan slice type for the extent of extents<​IndexType, Extents...>.
Preconditions: For each rank index k of src, slices...[k] is a valid submdspan slice for the extent of src.
Let SubExtents be a specialization of extents such that:
  • SubExtents​::​rank() equals MAP_RANK(slices, Extents​::​rank()); and
  • for each rank index k of Extents such that the type of slices...[k] is not a collapsing slice type, SubExtents​::​static_extent(MAP_RANK(slices, k)) equals the following, where denotes the type of slices...[k]:
    • Extents​::​static_extent(k) if denotes the full_extent_t; otherwise
    • 0, if is a specialization of strided_slice and ​::​extent_type denotes constant_wrapper<IndexType(0)>; otherwise
    • 1 + ((​::​extent_type​::​value - 1) / ​::​stride_type​::​value), if is a specialization of strided_slice whose extent_type and stride_type denote specializations of constant_wrapper;
    • otherwise, dynamic_extent.
Returns: A value ext of type SubExtents such that for each rank index k of extents<IndexType, Extents...>, where the type of slices...[k] is not a collapsing slice type, ext.extent(MAP_RANK(slices, k)) equals the following, where denotes slices...[k]:
  • .extent == 0 ? 0 : 1 + (.extent - 1) / .stride if the type of is a specialization of strided_slice,
  • otherwise, , where [L, U) is the submdspan slice range of for the extent of src.

23.7.3.7.7 Specializations of submdspan_mapping [mdspan.sub.map]

23.7.3.7.7.1 Sliceable layout mapping requirements [mdspan.sub.map.sliceable]

Let:
  • M denote a layout mapping class;
  • IT denote M​::​extent_type​::​index_type;
  • m denote a value of type (possibly const) M;
  • M_rank be equal to M​::​extent_type​::​rank();
  • valid_slices denote a pack of (possibly const) objects for which sizeof...(valid_slices) == M_rank is true and, for each rank index i of m.extents(), valid_slices...[i] is a valid submdspan slice for the extent of m.extents();
  • invalid_slices denote a pack of objects for which sizeof...(invalid_slices) == M_rank is true and there exists an integer k such that the cv-unqualified type of invalid_slices...[k] is none of the following:
For the purpose of this section, the meaning of submdspan_mapping is established as if by performing argument-dependent lookup only ([basic.lookup.argdep]).
A type M meets the sliceable layout mapping requirements if
  • M meets the layout mapping requirements ([mdspan.layout.policy.reqmts]),
  • the expression submdspan_mapping(m, invalid_slices...) is ill-formed, and
  • the following expression is well-formed and has the specified semantics: submdspan_mapping(m, valid_slices...)
Result: A type SMR that is a specialization of type submdspan_mapping_result<SM> for some type SM such that
  • SM meets the layout mapping requirements ([mdspan.layout.policy.reqmts]),
  • SM​::​extents_type is a specialization of extents,
  • SM​::​extents_type​::​rank() equals MAP_RANK(valid_slices, M_rank), and
  • SM​::​extents_type​::​index_type denotes IT.
Returns: An object smr of type SMR such that
  • smr.mapping.extents() == submdspan_extents(m.extents(), valid_slices...) is true;
    and
  • for each integer pack i which is a multidimensional index in smr.mapping.extents(),
    smr.mapping(i...) + smr.offset == m(j) is true, where j is an integer pack such that
    • sizeof...(j) is equal to M_rank; and
    • for each rank index ρ of m.extents(), j...[ρ] is equal to the sum of
      • the lower bound of the submdspan slice range of valid_slices...[ρ] for extent ρ of m.extents(), and
      • zero if the type of valid_slices...[ρ] is a collapsing slice type, i...[MAP_RANK(valid_slices,ρ)] otherwise.
template<class LayoutMapping> concept sliceable-mapping = see below;
Let lm be an object of type LayoutMapping and let fe denote a pack of objects of type full_extent_t for which sizeof...(fe) == LayoutMapping​::​extents_type​::​rank() is true.
A type LayoutMapping satisfies sliceable-mapping if
  • the expression submdspan_mapping(m, fe...) is well-formed when treated as an unevaluated operand, and
  • the type of that expression is a specialization of submdspan_mapping_result.
A type LayoutMapping models sliceable-mapping if LayoutMapping meets the sliceable layout mapping requirements.

23.7.3.7.7.2 Common [mdspan.sub.map.common]

The following elements apply to all functions in [mdspan.sub.map].
Constraints: sizeof...(SpliceSpecifiers) equals extents_type​::​rank().
Mandates: For each rank index k of extents(), SliceSpecifiers...[k] is a valid submdspan slice type for the extent of Extents.
Preconditions: For each rank index k of extents(), slices...[k] is a valid slice for the extent of extents().
Let sub_ext be the result of submdspan_extents(extents(), slices...) and let SubExtents be decltype(sub_ext).
Let sub_strides be an array<SubExtents​::​index_type, SubExtents​::​rank()> such that for each rank index k of extents() for which the type of slices...[k] is not a collapsing slice type, sub_strides[MAP_RANK(slices,k)] equals:
  • stride(k) * s.stride if the type of s is a specialization of strided_slice and s.stride < s.extent is true, where s is slices...[k];
  • otherwise, stride(k).
Let ls be a pack of values of index_type, where the element equals the lower bound of the submdspan slice range of slices...[ρ] for extent ρ of extents().
If ls...[k] equals extents().extent(k) for any rank index k of extents(), then let offset be a value of type size_t equal to required_span_size().
Otherwise, let offset be a value of type size_t equal to operator()(ls...).

23.7.3.7.7.3 layout_left specialization of submdspan_mapping [mdspan.sub.map.left]

template<class Extents> template<class... SliceSpecifiers> constexpr auto layout_left::mapping<Extents>::submdspan-mapping-impl( SliceSpecifiers... slices) const -> see below;
Returns:
  • submdspan_mapping_result{*this, 0}, if Extents​::​rank() == 0 is true;
  • otherwise, submdspan_mapping_result{layout_left​::​mapping(sub_ext), offset},
    if SubExtents​::​rank() == 0 is true;
  • otherwise, submdspan_mapping_result{layout_left​::​mapping(sub_ext), offset}, if
    • for each k in the range [0, SubExtents​::​rank() - 1), SpliceSpecifiers...[k] denotes full_extent_t; and
    • for k equal to SubExtents​::​rank() - 1, SpliceSpecifiers...[k] is a unit-stride slice type;
    [Note 1: 
    If the above conditions are true, all SpliceSpecifiers...[k] with k larger than SubExtents​​::​rank​() - 1 are convertible to index_type.
    — end note]
  • otherwise, submdspan_mapping_result{layout_left_padded<S_static>::mapping(sub_ext, stride(u + 1)), offset} if for a value u for which is the smallest value p larger than zero for which SliceSpecifiers...[​p] is a unit-stride slice type, the following conditions are met:
    • SliceSpecifiers...[0] is a unit-stride slice type; and
    • for each k in the range [u + 1, u + SubExtents​::​rank() - 1), SliceSpecifiers...[k] denotes full_extent_t; and
    • for k equal to u + SubExtents​::​rank() - 1, SliceSpecifiers...[k] is a unit-stride slice type;
    and where S_static is:
    • dynamic_extent, if static_extent(k) is dynamic_extent for any k in the range [0, u + 1),
    • otherwise, the product of all values static_extent(k) for k in the range [0, u + 1);
  • otherwise, submdspan_mapping_result{layout_stride::mapping(sub_ext, sub_strides), offset}

23.7.3.7.7.4 layout_right specialization of submdspan_mapping [mdspan.sub.map.right]

template<class Extents> template<class... SliceSpecifiers> constexpr auto layout_right::mapping<Extents>::submdspan-mapping-impl( SliceSpecifiers... slices) const -> see below;
Returns:
  • submdspan_mapping_result{*this, 0}, if Extents​::​rank() == 0 is true;
  • otherwise, submdspan_mapping_result{layout_right​::​mapping(sub_ext), offset},
    if SubExtents​::​rank() == 0 is true;
  • otherwise, submdspan_mapping_result{layout_right​::​mapping(sub_ext), offset}, if
    • for each k in the range [rank_ - SubExtents​::​rank() + 1, rank_),
      SliceSpecifiers...[k] denotes full_extent_t; and
    • for k equal to rank_ - SubExtents​::​rank(), SliceSpecifiers...[k] is a unit-stride slice type;
    [Note 1: 
    If the above conditions are true, all SliceSpecifiers...[k] with
    are convertible to index_type.
    — end note]
  • otherwise, submdspan_mapping_result{layout_right_padded<S_static>::mapping(sub_ext, stride(rank_ - u - 2)), offset} if for a value u for which is the largest value p smaller than rank_ - 1 for which SliceSpecifiers...[p] is a unit-stride slice type, the following conditions are met:
    • for k equal to rank_ - 1, SliceSpecifiers...[k] is a unit-stride slice type; and
    • for each k in the range [rank_ - SubExtents​::​rank() - u + 1, rank_ - u - 1),
      SliceSpecifiers...[p] denotes full_extent_t; and
    • for k equal to rank_ - SubExtents​::​rank() - u, SliceSpecifiers...[k] is a unit-stride slice type;
    and where S_static is:
    • dynamic_extent, if static_extent(k) is dynamic_extent for any k in the range [rank_ - u - 1, rank_),
    • otherwise, the product of all values static_extent(k) for k in the range [rank_ - u - 1, rank_);
  • otherwise, submdspan_mapping_result{layout_stride::mapping(sub_ext, sub_strides), offset}

23.7.3.7.7.5 layout_stride specialization of submdspan_mapping [mdspan.sub.map.stride]

template<class Extents> template<class... SliceSpecifiers> constexpr auto layout_stride::mapping<Extents>::submdspan-mapping-impl( SliceSpecifiers... slices) const -> see below;
Returns:
  • submdspan_mapping_result{*this, 0}, if Extents​::​rank() == 0 is true;
  • otherwise, submdspan_mapping_result{layout_stride::mapping(sub_ext, sub_strides), offset}

23.7.3.7.7.6 layout_left_padded specialization of submdspan_mapping [mdspan.sub.map.leftpad]

template<class Extents> template<class... SliceSpecifiers> constexpr auto layout_left_padded::mapping<Extents>::submdspan-mapping-impl( SliceSpecifiers... slices) const -> see below;
Returns:
  • submdspan_mapping_result{*this, 0}, if Extents​::​rank() == 0 is true;
  • otherwise, submdspan_mapping_result{layout_left​::​mapping(sub_ext), offset}, if rank_ == 1 is true or SubExtents​::​rank() == 0 is true;
  • otherwise, submdspan_mapping_result{layout_left​::​mapping(sub_ext), offset}, if
    • SubExtents​::​rank() == 1 is true and
    • SliceSpecifiers...[0] is a unit-stride slice type;
  • otherwise, submdspan_mapping_result{layout_left_padded<S_static>::mapping(sub_ext, stride(u + 1)), offset} if for a value u for which u + 1 is the smallest value p larger than zero for which SliceSpecifiers​...[p] is a unit-stride slice type, the following conditions are met:
    • SliceSpecifiers...[0] is a unit-stride slice type; and
    • for each k in the range [u + 1, u + SubExtents​::​rank() - 1), SliceSpecifiers...[k] denotes full_extent_t; and
    • for k equal to u + SubExtents​::​rank() - 1, SliceSpecifiers...[k] is a unit-stride slice type;
    where S_static is:
    • dynamic_extent, if static-padding-stride is dynamic_extent or static_extent(k) is dynamic_extent for any k in the range [1, u + 1),
    • otherwise, the product of static-padding-stride and all values static_extent(k) for k in the range [1, u + 1);
  • otherwise, submdspan_mapping_result{layout_stride::mapping(sub_ext, sub_strides), offset}

23.7.3.7.7.7 layout_right_padded specialization of submdspan_mapping [mdspan.sub.map.rightpad]

template<class Extents> template<class... SliceSpecifiers> constexpr auto layout_right_padded::mapping<Extents>::submdspan-mapping-impl( SliceSpecifiers... slices) const -> see below;
Returns:
  • submdspan_mapping_result{*this, 0}, if rank_ == 0 is true;
  • otherwise, submdspan_mapping_result{layout_right​::​mapping(sub_ext), offset},
    if rank_ == 1 is true or SubExtents​::​rank() == 0 is true;
  • otherwise, submdspan_mapping_result{layout_right​::​mapping(sub_ext), offset}, if
    • SubExtents​::​rank() == 1 is true and
    • for k equal to rank_ - 1, SliceSpecifiers...[k] is a unit-stride slice type;
  • otherwise, submdspan_mapping_result{layout_right_padded<S_static>::mapping(sub_ext, stride(rank_ - u - 2)), offset} if for a value u for which rank_ - u - 2 is the largest value p smaller than rank_ - 1 for which SliceSpecifiers...[p] is a unit-stride slice type, the following conditions are met:
    • for k equal to rank_ - 1, SliceSpecifiers...[k] is a unit-stride slice type; and
    • for each k in the range [rank_ - SubExtents​::​rank() - u + 1, rank_ - u - 1),
      SliceSpecifiers...[k] denotes full_extent_t; and
    • for k equal to rank_ - SubExtents​::​rank() - u, SliceSpecifiers...[k] is a unit-stride slice type;
    and where S_static is:
    • dynamic_extent if static-padding-stride is dynamic_extent or for any k in the range [rank_ - u - 1, rank_ - 1) static_extent(k) is dynamic_extent,
    • otherwise, the product of static-padding-stride and all values static_extent(k) with k in the range [rank_ - u - 1, rank_ - 1);
  • otherwise, submdspan_mapping_result{layout_stride::mapping(sub_ext, sub_strides), offset}

23.7.3.7.8 submdspan function template [mdspan.sub.sub]

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;
Let index_type be typename Extents​::​index_type.
Let slices be the pack introduced by the following declaration: auto [...slices] = submdspan_canonicalize_slices(src, raw_slices...);
Let sub_map_offset be the result of submdspan_mapping(src.mapping(), slices...).
[Note 1: 
This invocation of submdspan_mapping selects a function call via overload resolution on a candidate set that includes the lookup set found by argument-dependent lookup ([basic.lookup.argdep]).
— end note]
Constraints:
Mandates: For each rank index k of src:
  • SliceSpecifiers...[k] is a submdspan slice type for index_type, and
  • decltype(slices...[k]) is a valid submdspan slice type for the extent of Extents.
Preconditions: For each rank index k of src.extents(), slices...[k] is a valid submdspan slice for the extent of src.extents().
Effects: Equivalent to: auto sub_map_result = submdspan_mapping(src.mapping(), slices...); return mdspan(src.accessor().offset(src.data_handle(), sub_map_result.offset), sub_map_result.mapping, typename AccessorPolicy::offset_policy(src.accessor()));
[Example 1: 
Given a rank-3 mdspan grid3d representing a three-dimensional grid of regularly spaced points in a rectangular prism, the function zero_surface sets all elements on the surface of the 3-dimensional shape to zero.
It does so by reusing a function zero_2d that takes a rank-2 mdspan.
// zero out all elements in an mdspan template<class T, class E, class L, class A> void zero_2d(mdspan<T, E, L, A> a) { static_assert(a.rank() == 2); for (int i = 0; i < a.extent(0); i++) for (int j = 0; j < a.extent(1); j++) a[i, j] = 0; } // zero out just the surface template<class T, class E, class L, class A> void zero_surface(mdspan<T, E, L, A> grid3d) { static_assert(grid3d.rank() == 3); zero_2d(submdspan(grid3d, 0, full_extent, full_extent)); zero_2d(submdspan(grid3d, full_extent, 0, full_extent)); zero_2d(submdspan(grid3d, full_extent, full_extent, 0)); zero_2d(submdspan(grid3d, grid3d.extent(0) - 1, full_extent, full_extent)); zero_2d(submdspan(grid3d, full_extent, grid3d.extent(1) - 1, full_extent)); zero_2d(submdspan(grid3d, full_extent, full_extent, grid3d.extent(2) - 1)); } — end example]