[RFC] Report missing move in mapping::operator().
Luc Grosheintz
luc.grosheintz@gmail.com
Mon Jul 21 10:50:45 GMT 2025
Dear libstdc++ developers,
While writing the email to report the issue that mapping::operator()
doesn't use move, I noticed one other case in the C++26 padded layouts.
Therefore, I'd like to put the text up here first. Any feedback would be
highly welcome.
Kind regards,
Luc
--------------------------------------------------------------------------------
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:
- a ctor of extents,
- a ctor of mdspan,
- in 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 Section 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 & 7 in the Section
Resolution.
Resolution:
1. [mdspan.layout.left.obs] Add 'std::move' such that Effects: reads as
template<class... Indices>
constexpr index_type operator()(Indices... i) const noexcept;
[...]
Effects: [...] Equivalent to:
return ((static_cast<index_type>(std::move(i) * stride(P)) + ... + 0);
2. [mdspan.layout.right.obs] Analogous to 1.
3. [mdspan.layout.stride.obs] Analogous to 1.
4. [mdspan.layout.leftpad.obs] Analogous to 1; however i is called idxs.
5. [mdspan.layout.rightpad.obs] Analogous to 4.
6. [mdspan.layout.leftpad.cons] Rename the argument pad to padding and
introduce a variable `pad` for expositional purposes.
template<class OtherIndexType>
constexpr mapping(const extents_type& ext, OtherIndexType padding);
<ins>Let pad = static_cast<index_type>(std::move(padding)).</ins>
Constraints:
— is_convertible_v<OtherIndexType, index_type> is true.
— is_nothrow_constructible_v<index_type, OtherIndexType> is true.
Preconditions:
— pad<ins>ding</ins> is representable as a value of type index_type.
— <del>extents_type::index-cast(pad)</del><ins>pad</ins> is greater than
zero.
— If rank_ is greater than one, then LEAST-MULTIPLE-AT-LEAST(pad,
ext.extent(0)) is representable as a value of type index_type.
— 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.
— If padding_value is not equal to dynamic_extent, padding_value equals
<del>extents_type::index-cast(pad)</del><ins>pad</ins>.
Effects: Direct-non-list-initializes extents_ with ext, and if rank_ is
greater than one, direct-non-list-initializes stride-1 with
LEAST-MULTIPLE-AT-LEAST(pad, ext.extent(0)).
7. [mdspan.layout.rightpad.cons] Analogous to 6, but not literally, due
variable names in the context differing slightly.
More information about the Libstdc++
mailing list