Title
Bullet (4.3) preconditions are too strict to implement `submdspan`
Status
ready
Section
[mdspan.layout.stride.cons]
Submitter
Mark Hoemmen

Created on 2026-07-15.00:00:00 last changed 1 week ago

Messages

Date: 2026-07-31.16:46:35

Proposed resolution:

This wording is relative to N5054 with modifications from LWG 4603.

  1. Modify [mdspan.layout.stride.cons] as indicated:

    template<class OtherIndexType>
      constexpr mapping(const extents_type& e, span<OtherIndexType, rank_> s) noexcept;
    template<class OtherIndexType>
      constexpr mapping(const extents_type& e, const array<OtherIndexType, rank_>& s) noexcept;
    

    -3- Constraints: […]

    -4- Preconditions:

    • (4.1) — extents_type::index-cast(s[i]) is non-negative and representable as a value of type index_type ([basic.fundamental]) for all i in the range [0, rank_).

    • (4.2) — REQUIRED-SPAN-SIZE(e, s) is representable as a value of type index_type.

    • (4.3) — If both rank_ and the size of the multidimensional index space extents() are greater than 0, then there exists a permutation P of the integers in the range [0, rank_) and value m, such that

      • (4.3.1) — e.extent(i) == 1 is true for all i in the range [0, m),

      • (4.3.2) — e.extent(i) > 1 is true for all i in the range [m, rank_),

      • (4.3.3) — if m < rank_ is true, then s[p0m] > 0 is true and for all i in the range [m+1, rank_), s[pi] >= is greater than sum of s[pi-1j] * (e.extent(pij)-1) is true for all ij in the range [1, rank_) [m, i),

      where pk is the kth element of P.

    [Note: For layout_stride, this condition is necessary and sufficient for is_unique() to be true. — end note]

    -5- Effects: […]

Date: 2026-07-31.00:00:00

[ 2026-07-31 LWG telecon; Status changed: New → Ready. ]

Date: 2026-07-15.00:00:00

[ 2026-07-24; Tomasz provides updated wording. ]

Patch implementing the proposed resolution can be found here.

The note is adjusted as below condition is sufficient, but not necessary to guarantee uniqueness, consider strides (3, 5) and extents (5, 3). They do not meet the requirements but produce unique mapping:

    0   1   2   3   4
----------------------	
0|  0   3   6   9  12
1|  5   8  11  14  17
2| 10  13  16  19  22
Date: 2026-07-28.16:41:22

For `layout_stride::mapping`'s constructor that takes an extents object and an array (or span) of strides, the preconditions on the strides are too strict. This makes it impossible to implement `submdspan`.

For an example see here. Let `x` be an `mdspan` such that `x.mapping()` is `layout_right::mapping` with extents (2, 5).

// slice denotes indices {0,2,4} out of original {0,1,2,3,4}.
std::extent_slice slice{.offset=0, .extent=3, .stride=2};
auto x_sub = std::submdspan(x, std::full_extent, slice);
assert(x_sub.extent(0) == 2);
assert(x_sub.extent(1) == 3);
assert(x_sub.stride(0) == 5);
assert(x_sub.stride(1) == 2);

x_sub's `layout_type` is `layout_stride`. However, the only way to construct `layout_stride::mapping` with these extents and strides would be to call the constructor that takes an extents object and an array (or span) of strides, and the above extents and strides would violate the preconditions.

// Extents and strides violate precondition of layout_stride::mapping
// constructor ([mdspan.layout.stride.cons] 4.3).
assert(! (x_sub.stride(1) >= x_sub.stride(0) * x_sub.extent(0)));
assert(! (x_sub.stride(0) >= x_sub.stride(1) * x_sub.extent(1)));

The fix is to relax the preconditions so that they test only the possibility of overlap.

assert(! (x_sub.stride(1) >= 1 + (x_sub.extent(0) - 1) * x_sub.stride(0)));
assert(   x_sub.stride(0) >= 1 + (x_sub.extent(1) - 1) * x_sub.stride(1));

This wording is relative to N5054.

[Drafting note: The proposed wording currently misses to account for extents of zero or 1]

  1. Modify [mdspan.layout.stride.cons] as indicated:

    template<class OtherIndexType>
      constexpr mapping(const extents_type& e, span<OtherIndexType, rank_> s) noexcept;
    template<class OtherIndexType>
      constexpr mapping(const extents_type& e, const array<OtherIndexType, rank_>& s) noexcept;
    

    -3- Constraints: […]

    -4- Preconditions:

    1. (4.1) — The result of converting s[i] to `index_type` is greater than `0` for all i in the range [0, rank_).

    2. (4.2) — REQUIRED-SPAN-SIZE(e, s) is representable as a value of type `index_type` ([basic.fundamental]).

    3. (4.3) — If rank_ is greater than `0`, then there exists a permutation P of the integers in the range [0, rank_), such that s[pi] >= s[pi-1] * e.extent(pi-1) s[pi] >= 1 + (e.extent(pi-1) - 1) * s[pi-1] is `true` for all i in the range [1, rank_), where pi is the ith element of P.

      [Note 1: For `layout_stride`, this condition is necessary and sufficient for `is_unique()` to be `true`. — end note]

    -5- Effects: Direct-non-list-initializes extents_ with `e`, and for all d in the range [0, rank_), direct-non-list-initializes strides_[d] with as_const(s[d]).

History
Date User Action Args
2026-07-31 16:46:35adminsetmessages: + msg16547
2026-07-31 16:46:35adminsetstatus: new -> ready
2026-07-28 16:41:22adminsetmessages: + msg16533
2026-07-18 14:22:55adminsetmessages: + msg16526
2026-07-15 00:00:00admincreate