Created on 2026-10-17.00:00:00 last changed 1 week ago
Proposed resolution:
This wording is relative to N5054.
Modify [mdspan.sub.map.leftpad] as indicated:
[Drafting note: The case rank_ == 1 with a unit-stride slice and `SubExtents::rank() == 1` is covered by the third bullet (`SubExtents::rank() == 1` and `SliceSpecifiers...[0]` is a unit-stride slice type, same result); a strided slice falls to the last bullet and yields the `layout_stride` mapping.]
template<class Extents> template<class... SliceSpecifiers> constexpr auto layout_left_padded::mapping<Extents>::submdspan-mapping-impl( SliceSpecifiers... slices) const -> see below;-1- Returns:
(1.1) — `submdspan_mapping_result{*this, 0}`, if `Extents::rank() == 0` is `true`;
(1.2) — otherwise, `submdspan_mapping_result{layout_left::mapping(sub_ext), offset}`, if
rank_ == 1 is `true` or`SubExtents::rank() == 0` is `true`;
Modify [mdspan.sub.map.rightpad] as indicated:
template<class Extents> template<class... SliceSpecifiers> constexpr auto layout_right_padded::mapping<Extents>::submdspan-mapping-impl( SliceSpecifiers... slices) const -> see below;-1- Returns:
(1.1) — `submdspan_mapping_result{*this, 0}`, if rank_ == 0 is `true`;
(1.2) — otherwise, `submdspan_mapping_result{layout_right::mapping(sub_ext), offset}`, if
rank_ == 1 is `true` or`SubExtents::rank() == 0` is `true`;
Modify [mdspan.sub.map.sliceable] as indicated:
-3- A type `M` meets the sliceable layout mapping requirements if
[…] -4- Result: A type `SMR` that is a specialization of type submdspan_mapping_result<SM> for some type `SM` such that […] -5- Returns: An object `smr` of type `SMR` such that
(5.1) — `smr.mapping.extents() == subextents(m.extents(), valid_slices...)` is `true`; and
(5.2) — 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
(5.2.1) — `sizeof...(j)` is equal to `M_rank`; and
(5.2.2) — for each rank index ρ of `m.extents()`, `j...[ρ]` is equal to the sum of
(5.2.2.1) — the lower bound of the `submdspan` slice range of `valid_slices...[ρ]` for extent ρ of `m.extents()`, and
(5.2.2.2) — zero if the type of `valid_slices...[ρ]` is a collapsing slice type,
`i...[MAP_RANK(valid_slices,ρ)]`otherwise, the product of `i...[MAP_RANK(valid_slices,ρ)]` and `s.stride` if the type of `valid_slices...[ρ]` is a specialization of `extent_slice` and s.extent > 1 is `true`, where `s` is `valid_slices...[ρ]`, and `i...[MAP_RANK(valid_slices,ρ)]` otherwise.
[mdspan.sub.map.leftpad] (and its mirror [mdspan.sub.map.rightpad]) specify the result of layout_left_padded::mapping<Extents>::submdspan-mapping-impl(slices...). Bullet (1.2) of the Returns element reads:
otherwise, `submdspan_mapping_result{layout_left::mapping(sub_ext), offset}`, if rank_== 1 is `true` or `SubExtents::rank() == 0` is `true`;
The first part of the condition applies to every slice of a rank-one mapping, including a strided one. For rank_ == 1 and the slice extent_slice{0, cw<4>, cw<2>} the result has `SubExtents::rank() == 1` with extent 2, and `layout_left::mapping(sub_ext)` has stride 1. The returned mapping therefore addresses the first two elements `0, 1` (plus `offset`) instead of elements `0, 2`.
The general rule of [mdspan.sub.map.common] gives the intended meaning: for a slice `s` that is an `extent_slice` with `s.extent > 1`, the stride of the corresponding sub-dimension is `stride(k) * s.stride` (the list that defines `sub_strides`), so the strided slice yields a `layout_stride` mapping with stride 2. The `layout_left`, `layout_right` and `layout_stride` specializations ([mdspan.sub.map.left], [mdspan.sub.map.right], [mdspan.sub.map.stride]) are unaffected; only the padded specializations contain the rank_ == 1 shortcut. Unit-stride slices are unaffected. The generic requirement that is meant to catch this does not do so: [mdspan.sub.map.sliceable] Returns (5.2) requires`smr.mapping(i...) + smr.offset == m(j)`, where `j...[ρ]` is equal to the sum of the lower bound of the `submdspan` slice range ... and zero if the type of `valid_slices...[ρ]` is a collapsing slice type, `i...[MAP_RANK(valid_slices,ρ)]` otherwise.
The index `i` is added to the lower bound without being multiplied by the slice stride. Read literally, the (stride-1) `layout_left::mapping` above satisfies this postcondition, while a correct `layout_stride` result for the same slice (the result that [mdspan.sub.map.common] itself prescribes for the default mappings) does not. The two defects hide each other: the postcondition can neither detect the wrong rank-one result nor accept the right one.
Reproducer: layout_left_padded<dynamic_extent>::mapping<extents<int, 8>> with submdspan_mapping(m, extent_slice{0, cw<4>, cw<2>}): `smr.mapping(1) + smr.offset` is `1`, `m(2)` is the element the slice selects as its second element.| History | |||
|---|---|---|---|
| Date | User | Action | Args |
| 2026-10-31 13:50:57 | admin | set | messages: + msg16635 |
| 2026-10-17 00:00:00 | admin | create | |