C++ Logo

std-proposals

Advanced search

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

From: Mark Hoemmen <mark.hoemmen_at_[hidden]>
Date: Wed, 1 Apr 2026 11:51:14 -0600
Hewill Kang wrote:
> Hi, I wrote a paper on this: https://isocpp.org/files/papers/P4173R0.html

Hi and thanks for your interest in mdspan!

I do think a "random access iterator accessor" would be useful. I've
actually implemented this for some of my colleagues, who asked for a
proposal like this.

Have you considered adding converting constructors so that users can
go from an iterator-to-nonconst to an iterator-to-const?

I generally approve of this proposal. My only concern is that the
non-wording sections of the paper say some things about mdspan's
design that I don't think are quite accurate.

> Standard mdspan accessors are primarily limited to raw pointers, hindering direct integration with non-contiguous C++ Ranges and proxy-based containers.

It's true that default_accessor and aligned_accessor work on pointers.
However, mdspan's design actively encourages users to write custom
accessors. Just like the Standard can't foresee all possible
containers, the Standard can't foresee all possible custom accessors
that users might like to write for their custom hardware. That's why
it's a customization point. There are a few other places in the paper
that say things like this. Would you consider correcting them?

> ... hindering direct integration with non-contiguous C++ Ranges

mdspan integrates perfectly well with ranges.

template<class ElementType, class Extents, class Layout, class Accessor>
constexpr auto to_index_range(extents<IndexType, Exts...> e) {
  auto [...index_views] =
    [] <size_t... Inds> (const auto& exts, index_sequence<Inds>) {
      return tuple{views::indices(exts.extent(Inds))...};
    } (e, make_index_sequence<sizeof...(Exts)>());
  return views::cartesian_product(index_views...);
}

template<class ElementType, class Extents, class Layout, class Accessor>
constexpr auto to_range(mdspan<ElementType, Extents, Layout, Accessor> x) {
  if constexpr (std::is_same_v<Layout, layout_right>) {
    return views::indices(x.required_span_size()) |
      views::transform(
        [acc = x.accessor(), handle = x.data_handle()] (size_t k) {
          return acc.access(handle, k);
        });
  } else {
    return to_index_range(x.extents()) | views::transform([=] (auto t) {
      auto [...indices] = t;
      return x[indices...];
    });
  }
}

There, now you have `to_range(x)`. I haven't proposed this for the
Standard for two reasons.

1. What would it mean to `std::copy` from a 2 x 3 x 5 x 7 mdspan to a
6 x 15 x 7 mdspan?

2. Compilers are unlikely to be able to optimize a "multidimensional iterator."

3. It might be a surprising pessimization that `layout_left` mdspan
would still be iterated in row-major order.

It's important to understand WHY mdspan uses accessors instead of
iterators. The design intent is that accessors are easy to write for
hardware experts who are not C++ experts, because it's much more
likely that users will want to write a custom accessor (to support the
huge variety of custom memory access patterns and network hardware
that exists in practice) than it is that they will want to write a
custom layout mapping. Iterators are hard to write correctly -- hard
enough that proposals like Zach Laine's P2727 exist to make this
easier.

All that being said, I don't object to the proposal! I just want to
help clear up the design intent of mdspan.

Thanks!
mfh

Received on 2026-04-01 17:51:30