C++ Logo

std-proposals

Advanced search

Re: [std-proposals] `random_access_iterator_accessor` for `std::mdspan`?

From: Mark Hoemmen <mark.hoemmen_at_[hidden]>
Date: Sun, 5 Apr 2026 22:28:47 -0600
On Sat, Apr 4, 2026 at 10:28 AM Hewill Kang <hewillk_at_[hidden]> wrote:
>>
>> Currently, the creation of `mdspans` with `iterator_accessor` relies on the following CTAD:
>> ```
>> template<class MappingType, class AccessorType>
>> mdspan(const typename AccessorType::data_handle_type&, const MappingType&,
>> const AccessorType&)
>> -> mdspan<typename AccessorType::element_type, typename MappingType::extents_type,
>> typename MappingType::layout_type, AccessorType>;
>> ```
>> We must spell with
>> ```
>> auto _3x3 = std::layout_right::mapping(std::extents(3, 3));
>> auto ms = std::mdspan(r.begin(), _3x3, std::iterator_accessor(r.begin()));

Not quite. The accessor is stateless in this case.

auto ms = std::mdspan(r.begin(), _3x3,
std::iterator_accessor<decltype(r.begin())>());

Accessors don't see the data handle until first use. (P2897,
aligned_accessor, talks a bit about how this relates to the
possibility of checking the data handle's preconditions.)

> Typing `r.begin()` twice is unsatisfactory to me.
> I think we can provide an additional constructor for `mdspan` to indicate that it is constructed from ranges:
>
> ```
> template<ranges::random_access_range Range, class... OIndexTypes>
> requires ranges::borrowed_range<Range>
> constexpr explicit
> mdspan(from_range_t, Range&& r, OIndexTypes... exts) : ptr_(ranges::begin(r)), ...
> ```
>
> Along with the following CTAD:
>
> ```
> template<ranges::random_access_range Range, class... Integrals>
> explicit mdspan(from_range_t, Range&&, Integrals...)
> -> mdspan<typename iterator_accessor<ranges::iterator_t<Range>>::element_type,
> extents<size_t, maybe-static-ext<Integrals>...>,
> layout_right,
> iterator_accessor<ranges::iterator_t<Range>>>
> ```
>
> This allows the user to spell it out simply with:
>
> ```
> vector x_coords = {0, 10, 20};
> vector y_coords = {0, 5};
> vector z_coords = {15, 10, 5, 20};
> auto r = views::cartesian_product(x_coords, y_coords, z_coords);
> auto ms1 = mdspan(from_range, r, x_coords.size(), y_coords.size(), z_coords.size());
>
> std::vector posX = {0.0, 1.0, 2.0, 3.0};
> std::vector posY = {0.0, 0.5, 1.0, 1.5};
> std::vector mask = {1, 0, 1, 0};
> auto ms2 = mdspan(from_range, std::views::zip(posX, posY, mask), 2, 2);
> ```
>
> I think this clearly shows that the user intends to make mdspans from ranges rather than a raw pointer.

I appreciate your thinking and your wish to make C++ better : - ) .

Using `from_range` might suggest that a container is being constructed
"from" a range. That's not what's happening here. The mdspan is a
view of the range itself. It's not "from" a range, it IS the range,
just a multidimensional view of it.

[range.utility.conv.general] 1: "The range conversion functions
construct an object (usually a container) from a range, by using a
constructor taking a range, a from_range_t tagged constructor, or a
constructor taking a pair of iterators, or by inserting each element
of the range into the default-constructed object."

> And there is no spelling of .begin(), and no duplicate spelling, which looks satisfactory to me.
> What do you think?

Have you considered just adding nonmember functions that create an
mdspan with the desired properties, such as the following?

template<class Range, size_t... Extents> requires( /* ... */ )
constexpr auto make_mdspan_from_range(Range&& r, Extents... exts) {
  auto beg = r.begin();
  return mdspan(beg, layout_right::mapping{exts...},
iterator_accessor<decltype(beg)>());
}

Why is it so important to add a special-case constructor to mdspan for
this use case?

mfh

>
> Hewill
>
>
> Hewill Kang <hewillk_at_[hidden]> 於 2026年4月4日週六 下午5:22寫道:
>>
>> Currently, the creation of `mdspans` with `iterator_accessor` relies on the following CTAD:
>> ```
>> template<class MappingType, class AccessorType>
>> mdspan(const typename AccessorType::data_handle_type&, const MappingType&,
>> const AccessorType&)
>> -> mdspan<typename AccessorType::element_type, typename MappingType::extents_type,
>> typename MappingType::layout_type, AccessorType>;
>> ```
>>
>> We must spell with
>> ```
>> auto _3x3 = std::layout_right::mapping(std::extents(3, 3));
>> auto ms = std::mdspan(r.begin(), _3x3, std::iterator_accessor(r.begin()));
>> ```
>>
>> A subsequent question is, once we have an `iterator_accessor`, should we enhance mdspan's CTAD so that:
>>
>> ```
>> auto ms = std::mdspan(r.begin(), 3, 3); // call begin() instead of data()
>> ```
>>
>> Works well? For example, enhance `ElementType*` parts in the current CTAD for `random_access_iterator`:
>> ```
>> template<random_access_iterator I, class... Integrals>
>> requires ((is_convertible_v<Integrals, size_t> && ...) && sizeof...(Integrals) > 0)
>> explicit mdspan(I, Integrals...)
>> -> mdspan<typename iterator_accessor<I>::element_type,
>> extents<size_t, maybe-static-ext<Integrals>...>,
>> layout_right,
>> iterator_accessor<I>>;
>> ```
>> This eliminates the need for spelling of r.begin() twice.
>> However, it's uncertain whether this relaxation for users dealing with contiguous_iterator/random_access_iteratorer instead of a raw pointer is good.
>>
>> Hewill Kang <hewillk_at_[hidden]> 於 2026年4月4日週六 上午11:54寫道:
>>>
>>> Thank you, that makes sense.
>>> I updated the paper.
>>>
>>>
>>>
>>>
>>> Mark Hoemmen <mark.hoemmen_at_[hidden]> 於 2026年4月4日週六 上午3:16寫道:
>>>>
>>>> On Fri, Apr 3, 2026 at 11:27 AM Hewill Kang <hewillk_at_[hidden]> wrote:
>>>> >>
>>>> >> Also, iterator_accessor has to deal with pointers as a special case of
>>>> >> iterators, so it should have the same safeguard as default_accessor and
>>>> >> friends against converting a pointer-to-derived to a pointer-to-base.
>>>> >
>>>> >
>>>> > That's reasonable, thank you. I updated the paper.
>>>> >
>>>> > This is the current final version of the paper: https://isocpp.org/files/papers/P4173R0.html
>>>> > And implementation with libstdc++: https://godbolt.org/z/86MKa3oa5
>>>>
>>>> Would you consider making those conversion operators to
>>>> `default_accessor` explicit?
>>>>
>>>> I ask because container authors sometimes provide iterators that model
>>>> `contiguous_iterator` but do run-time bounds checking. Implicit
>>>> conversion from `iterator_accessor` to `default_accessor` would
>>>> silently strip away this protection.
>>>>
>>>> Thanks!
>>>> mfh
>>>>
>>>>
>>>> >
>>>> > Thanks.
>>>> > Hewill
>>>> >
>>>> > Ell <ell.ell.se_at_[hidden]> 於 2026年4月3日週五 上午2:48寫道:
>>>> >>
>>>> >> On Thursday, April 2nd, 2026 at 8:24 PM, Mark Hoemmen via Std-Proposals <std-proposals_at_[hidden]> wrote:
>>>> >>
>>>> >> > >> Have you considered adding converting constructors so that users can
>>>> >> > >> go from an iterator-to-nonconst to an iterator-to-const?
>>>> >> > >
>>>> >> > > The proposed wording has the following converting constructors:
>>>> >> > >
>>>> >> > > template<convertible_to<I> I2>
>>>> >> > > constexpr iterator_accessor(iterator_accessor<I2>) noexcept {}
>>>> >> > >
>>>> >> > > I think it's enough to cover it?
>>>> >> >
>>>> >> > The analog of this is enough for `default_accessor`, but that only has
>>>> >> > to deal with pointers. If I is constructible from I2, but I2 is not
>>>> >> > convertible to I, then the same should be true of the accessor. As a
>>>> >> > result, mdspan conversion would work in the same way as accessor
>>>> >> > conversion. This is the design intent of mdspan.
>>>> >> >
>>>> >>
>>>> >> Also, iterator_accessor has to deal with pointers as a special case of
>>>> >> iterators, so it should have the same safeguard as default_accessor and
>>>> >> friends against converting a pointer-to-derived to a pointer-to-base.

Received on 2026-04-06 04:29:00