Title
Missing move in `mdspan` layout `mapping::operator()`
Status
new
Section
[mdspan.layout]
Submitter
Luc Grosheintz

Created on 2025-08-13.00:00:00 last changed 2 weeks ago

Messages

Date: 2025-08-16.13:52:51

Proposed resolution:

This wording is relative to N5014.

  1. Modify [mdspan.layout.left.obs] as indicated:

    template<class... Indices>
      constexpr index_type operator()(Indices... i) const noexcept;
    

    -2- Constraints: […]

    -3- Preconditions: […]

    -4- Effects: Let `P` be a parameter pack such that

    is_same_v<index_sequence_for<Indices...>, index_sequence<P...>>
    

    is `true`. Equivalent to:

    return ((static_cast<index_type>(std::move(i)) * stride(P)) + ... + 0);
    
  2. Modify [mdspan.layout.right.obs] as indicated:

    template<class... Indices>
      constexpr index_type operator()(Indices... i) const noexcept;
    

    -2- Constraints: […]

    -3- Preconditions: […]

    -4- Effects: Let `P` be a parameter pack such that

    is_same_v<index_sequence_for<Indices...>, index_sequence<P...>>
    

    is `true`. Equivalent to:

    return ((static_cast<index_type>(std::move(i)) * stride(P)) + ... + 0);
    
  3. Modify [mdspan.layout.stride.obs] as indicated:

    template<class... Indices>
      constexpr index_type operator()(Indices... i) const noexcept;
    

    -2- Constraints: […]

    -3- Preconditions: […]

    -4- Effects: Let `P` be a parameter pack such that

    is_same_v<index_sequence_for<Indices...>, index_sequence<P...>>
    

    is `true`. Equivalent to:

    return ((static_cast<index_type>(std::move(i)) * stride(P)) + ... + 0);
    
  4. Modify [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).

  5. Modify [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).

  6. Modify [mdspan.layout.leftpad.cons] as indicated:

    template<class OtherIndexType>
    constexpr mapping(const extents_type& ext, OtherIndexType padding);
    

    Let pad = static_cast<index_type>(std::move(padding)).

    -3- Constraints: […]

    -4- Preconditions:

    1. (4.1) — padding is representable as a value of type `index_type`.

    2. (4.2) — extents_type::index-cast(pad)pad is greater than zero.

    3. (4.3) — If rank_ is greater than one, then LEAST-MULTIPLE-AT-LEAST(pad, ext.extent(0)) is representable as a value of type `index_type`.

    4. (4.4) — If rank_ is greater than one, then the product of LEAST-MULTIPLE-AT-LEAST(pad, ext.extent(0)) and all values ext.extent(k) with k in the range of [1, rank_) is representable as a value of type `index_type`.

    5. (4.5) — If `padding_value` is not equal to `dynamic_extent`, `padding_value` equals extents_type::index-cast(pad)pad.

    -5- Effects: […]

  7. Modify [mdspan.layout.rightpad.cons] as indicated:

    template<class OtherIndexType>
    constexpr mapping(const extents_type& ext, OtherIndexType padding);
    

    Let pad = static_cast<index_type>(std::move(padding)).

    -3- Constraints: […]

    -4- Preconditions:

    1. (4.1) — padding is representable as a value of type `index_type`.

    2. (4.2) — extents_type::index-cast(pad)pad is greater than zero.

    3. (4.3) — If rank_ is greater than one, then LEAST-MULTIPLE-AT-LEAST(pad, ext.extent(rank_ - 1)) is representable as a value of type `index_type`.

    4. (4.4) — If rank_ is greater than one, then the product of LEAST-MULTIPLE-AT-LEAST(pad, ext.extent(rank_ - 1)) and all values ext.extent(k) with k in the range of [1, rank_ - 1) is representable as a value of type `index_type`.

    5. (4.5) — If `padding_value` is not equal to `dynamic_extent`, `padding_value` equals extents_type::index-cast(pad)pad.

    -5- Effects: […]

Date: 2025-08-13.00:00:00

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`.

They're either passed as an array/span of `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.

A different pattern can be found a ctor for `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++.

History
Date User Action Args
2025-08-16 13:52:51adminsetmessages: + msg14933
2025-08-13 00:00:00admincreate