This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.
mdspan layout mapping::operator()Section: 23.7.3.4 [mdspan.layout] Status: New Submitter: Luc Grosheintz Opened: 2025-08-13 Last modified: 2025-10-21
Priority: 2
View other active issues in [mdspan.layout].
View all other issues in [mdspan.layout].
View all issues with New status.
Discussion:
Numerous template classes in <mdspan> have template parameter IndexType.
While this template parameter is restricted to be a signed or unsigned integer,
these classes often accept user-defined classes that convert to IndexType.
OtherIndexType; or as a template parameter pack.
When passed as a template parameter pack, the common pattern is
template<class... OtherIndexTypes>
requires std::is_convertible_v<OtherIndexTypes, IndexType> && ...
void dummy(OtherIndexTypes... indices)
{
something(static_cast<IndexType>(std::move(indices))...);
}
This pattern allows passing in objects that convert to IndexType only as an rvalue reference, e.g.
class RValueInt
{
constexpr
operator int() && noexcept
{ return m_int; }
private:
int m_int;
};
This pattern can be found in:
a ctor of extents,
a ctor of mdspan,
mdspan::operator[].
The five standardized layout mappings use a different pattern in their operator(), namely,
static_cast<IndexType>(indices)...
This prevents the passing in objects of type RValueInt, because the
conversion isn't happening from an rvalue reference. This is addressed by
Items 1 - 5 in the Proposed Resolution.
layout_{left,right}_padded.
Namely, directly passing an object of type OtherIndexType to
LEAST-MULTIPLE-AT-LEAST. This is addressed in Items 6 and 7
in the Proposed Resolution.
This inconsistency was noticed while fixing
PR121061
and these changes have been applied to all layout mappings implemented in libstdc++.
[2025-10-21; Reflector poll.]
Set priority to 2 after reflector poll.
The resolution bypasses extents_type::index-cast that would
validate if input value is representable.
We should require convertibility without regard to const and value category.
Proposed resolution:
This wording is relative to N5014.
Modify 23.7.3.4.5.3 [mdspan.layout.left.obs] as indicated:
template<class... Indices> constexpr index_type operator()(Indices... i) const noexcept;-2- Constraints: […]
-3- Preconditions: […] -4- Effects: LetPbe a parameter pack such thatis_same_v<index_sequence_for<Indices...>, index_sequence<P...>>is
true. Equivalent to:return ((static_cast<index_type>(std::move(i)) * stride(P)) + ... + 0);
Modify 23.7.3.4.6.3 [mdspan.layout.right.obs] as indicated:
template<class... Indices> constexpr index_type operator()(Indices... i) const noexcept;-2- Constraints: […]
-3- Preconditions: […] -4- Effects: LetPbe a parameter pack such thatis_same_v<index_sequence_for<Indices...>, index_sequence<P...>>is
true. Equivalent to:return ((static_cast<index_type>(std::move(i)) * stride(P)) + ... + 0);
Modify 23.7.3.4.7.4 [mdspan.layout.stride.obs] as indicated:
template<class... Indices> constexpr index_type operator()(Indices... i) const noexcept;-2- Constraints: […]
-3- Preconditions: […] -4- Effects: LetPbe a parameter pack such thatis_same_v<index_sequence_for<Indices...>, index_sequence<P...>>is
true. Equivalent to:return ((static_cast<index_type>(std::move(i)) * stride(P)) + ... + 0);
Modify 23.7.3.4.8.4 [mdspan.layout.leftpad.obs] as indicated:
template<class... Indices> constexpr index_type operator()(Indices... idxs) const noexcept;-3- Constraints: […]
-4- Preconditions: […] -5- Returns:((static_cast<index_type>(std::move(idxs)) * stride(P_rank)) + ... + 0).
Modify 23.7.3.4.9.4 [mdspan.layout.rightpad.obs] as indicated:
template<class... Indices> constexpr index_type operator()(Indices... idxs) const noexcept;-3- Constraints: […]
-4- Preconditions: […] -5- Returns:((static_cast<index_type>(std::move(idxs)) * stride(P_rank)) + ... + 0).
Modify 23.7.3.4.8.3 [mdspan.layout.leftpad.cons] as indicated:
template<class OtherIndexType> constexpr mapping(const extents_type& ext, OtherIndexType padding);Let
-3- Constraints: […] -4- Preconditions:pad = static_cast<index_type>(std::move(padding)).
(4.1) —
paddingis representable as a value of typeindex_type.(4.2) —
pad is greater than zero.extents_type::index-cast(pad)(4.3) — If
rank_is greater than one, thenLEAST-MULTIPLE-AT-LEAST(pad, ext.extent(0))is representable as a value of typeindex_type.(4.4) — If
rank_is greater than one, then the product ofLEAST-MULTIPLE-AT-LEAST(pad, ext.extent(0))and all valuesext.extent(k)withkin the range of[1, rank_)is representable as a value of typeindex_type.(4.5) — If
padding_valueis not equal todynamic_extent,padding_valueequals.extents_type::index-cast(pad)pad-5- Effects: […]
Modify 23.7.3.4.9.3 [mdspan.layout.rightpad.cons] as indicated:
template<class OtherIndexType> constexpr mapping(const extents_type& ext, OtherIndexType padding);Let
-3- Constraints: […] -4- Preconditions:pad = static_cast<index_type>(std::move(padding)).
(4.1) —
paddingis representable as a value of typeindex_type.(4.2) —
pad is greater than zero.extents_type::index-cast(pad)(4.3) — If
rank_is greater than one, thenLEAST-MULTIPLE-AT-LEAST(pad, ext.extent(rank_ - 1))is representable as a value of typeindex_type.(4.4) — If
rank_is greater than one, then the product ofLEAST-MULTIPLE-AT-LEAST(pad, ext.extent(rank_ - 1))and all valuesext.extent(k)withkin the range of[1, rank_ - 1)is representable as a value of typeindex_type.(4.5) — If
padding_valueis not equal todynamic_extent,padding_valueequals.extents_type::index-cast(pad)pad-5- Effects: […]