This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of Ready status.

4606. Bullet (4.3) preconditions are too strict to implement submdspan

Section: 23.7.3.4.7.3 [mdspan.layout.stride.cons] Status: Ready Submitter: Mark Hoemmen Opened: 2026-07-15 Last modified: 2026-07-31

Priority: Not Prioritized

View other active issues in [mdspan.layout.stride.cons].

View all other issues in [mdspan.layout.stride.cons].

View all issues with Ready status.

Discussion:

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 (23.7.3.4.7.3 [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));
Previous resolution [SUPERSEDED]

This wording is relative to N5054.

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

  1. Modify 23.7.3.4.7.3 [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 (6.9.3 [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]).

[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

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

Proposed resolution:

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

  1. Modify 23.7.3.4.7.3 [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 (6.9.3 [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: […]