Date: Wed, 1 Apr 2026 10:49:18 -0400
Your four Examples are excellent. I do wish you'd present them in
Tony-table form, though: IIUC today each of the four examples is ill-formed
(rejected at compile-time), and after P4173 they'd all be well-formed and
Do The Right Thing, is that correct?
The main thing I worry about is that your proposal essentially nerfs any
optimization potential inside the implementation. You write:
> mdspan essentially treats multi-dimensional access as a layer on top of
contiguous memory, rather than a generalized abstraction for any indexed
data source.
But is it just "*treating*" multi-dimensional access as a layer on top of
contiguous memory, interface-wise; or does mdspan internally *implement*
multi-dimensional access as raw-pointer operations, in order to reduce code
bloat and to reduce pressure on the compiler's optimizer? I fear mdspan
does the latter.
So I fear that your proposal is basically asking implementors to implement
(certain parts of) mdspan twice: one time for contiguous iterators, which
can be lowered to raw pointers; and a second time (non-existent today, but
forced by your proposal) for random-access-but-non-contiguous iterators,
which will be forbidden to use any clever optimizations.
I'm not necessarily right about that fear; and even if implementors *are*
forced to implement two codepaths, there's certainly precedent in the STL
for that.
But basically this indicates that your D4173R0 is conspicuously missing an
"Implementation experience" section.
Nit: "Lastest" should be "Latest."
Bikeshed: "__cpp_lib_iterator_accessor" should perhaps be
"__cpp_lib_mdspan_iterator_accessor" for clarity.
Pre-existing nit: Your *Mandates* element has the words "is required to
be"; that should be just "is". Do you feel like submitting an editorial PR
for the pre-existing misuses of "is required to be" in [mdspan.accessor]?
You write:
> -3- Each specialization of iterator_accessor is a trivially copyable type
that models semiregular.
Is this "trivially copyable" implementable? *If iterator_accessor<I>
contains an I data member*, it's not. At least this should say
"iterator_accessor<I> is trivially copyable if I is trivially copyable."
Or, alternatively, add "*Mandates:* I is trivially copyable."
The latter would be really annoying IMHO. However, it brings me back to my
first fear: Is there a *reason* that all accessors are currently specified
to be trivially copyable? Does that help the implementor somewhere later
on, that we can copy (a contiguous array of) accessors with memcpy without
knowing their exact type? If so, then permitting `iterator_accessor<I>` to
be non-trivially copyable is going to be an implementation burden. So
again, I want to see an "Implementation Experience" section and I want to
see it deal with these (imagined/feared) optimization issues head-on.
Alternatively, *if iterator_accessor<I> does not contain an I data member,*
then why stop at "trivially copyable"? Why not specify that
iterator_accessor<I> is always an *empty* type? (And then, pre-existing,
why do none of the other accessors bother to specify that? What data
members are they imagined to have, then?)
Other than the big fear that this will turn out to be unimplementable for
some technical reason, IMHO this sounds great. Again, those four Examples
are very motivating.
HTH,
Arthur
On Wed, Apr 1, 2026 at 10:01 AM Hewill Kang via Std-Proposals <
std-proposals_at_[hidden]> wrote:
> Hi, I wrote a paper on this: https://isocpp.org/files/papers/P4173R0.html
> Thanks.
>
> Hewill Kang <hewillk_at_[hidden]> 於 2026年3月30日週一 下午8:33寫道:
>
>> Hi all,
>>
>> Currently, although `mdspan` is designed as a general-purpose multiview,
>> it mostly accepts a pointer and accesses different elements through a
>> pointer algorithm.
>> However, I am not satisfied with these *pointer*-based mandates because
>> I do not think they are much different from a general
>> `contiguous_iterator`, which can be a common iterator such as
>> `vector::iterator`.
>> If we look at the `default_accessor`, we can see the clue:
>>
>> ```cpp
>> template<class ElementType>
>> struct default_accessor {
>> using offset_policy = default_accessor;
>> using element_type = ElementType;
>> using reference = ElementType&;
>> using data_handle_type = ElementType*;
>>
>> constexpr default_accessor() noexcept = default;
>> constexpr reference access(data_handle_type p, size_t i) const
>> noexcept
>> { return p[i]; }
>> constexpr data_handle_type offset(data_handle_type p, size_t i) const
>> noexcept
>> { return p + i; }
>> };
>> ```
>> Then, upon closer inspection, we discovered that this is *exactly* the
>> operation supported by C++20 `random_access_iterator`, i.e.,
>> `operator[]` and `operator+`.
>> Given this, I think we can introduce a new accessor class that wraps the
>> `random_access_iterator`, for example:
>>
>> ```cpp
>> template<random_access_iterator I>
>> struct *iter_accessor* {
>> using offset_policy = iter_accessor;
>> using reference = iter_reference_t<I>;
>> using element_type = remove_reference_t<reference>;
>> using data_handle_type = I;
>>
>> iter_accessor() noexcept = default;
>> constexpr reference
>> access(data_handle_type p, std::iter_difference_t<I> i) const
>> { return p[i]; }
>> constexpr data_handle_type
>> offset(data_handle_type p, std::iter_difference_t<I> i) const
>> { return p + i; }
>> };
>> ```
>>
>> This fully inherits the natural characteristics of a
>> `random_access_iterator`, which allows us to very easily make `mdspan`s
>> with a wide variety of different `random_access_range`s, such as:
>>
>> ```
>> using Layout = std::layout_right::mapping<std::extents<int, 3, 3>>;
>>
>> auto r = std::views::iota(0, 9);
>> auto ms1 = std::mdspan(r.begin(), Layout{}, *iter_accessor*
>> <decltype(r.begin())>{});
>> /* 0 1 2
>> 3 4 5
>> 6 7 8 */
>>
>> auto v = std::vector<bool>{true, false, true, false, true, false,
>> true, false, true};
>> auto ms2 = std::mdspan(v.begin(), Layout{}, *iter_accessor*
>> <decltype(v.begin())>{});
>> /* true false true
>> false true false
>> true false true */
>>
>> std::vector<int> v1{1, 2, 3}, v2{4, 5}, v3{};
>> std::array a{6, 7, 8};
>> auto s = std::views::single(9);
>> auto r3 = std::views::concat(v1, v2, v3, a, s);
>> auto ms3 = std::mdspan(r3.begin(), Layout{}, *iter_accessor*
>> <decltype(r3.begin())>{});
>> /* 1 2 3
>> 4 5 6
>> 7 8 9 */
>> ```
>> Demo: https://godbolt.org/z/xb5vrejba
>>
>> I think this is a very useful utility.
>> Based on C++26, we have a large majority of range adaptors in `<ranges>`
>> that can support `random_access_range`.
>> Introducing this `accessor` makes them better integrated with `mdspan`,
>> which also opens the door for third-party custom range types.
>>
>> What do you think? Appreciate any feedback.
>>
>> Hewill
>>
>>
>>
>>
>> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
Tony-table form, though: IIUC today each of the four examples is ill-formed
(rejected at compile-time), and after P4173 they'd all be well-formed and
Do The Right Thing, is that correct?
The main thing I worry about is that your proposal essentially nerfs any
optimization potential inside the implementation. You write:
> mdspan essentially treats multi-dimensional access as a layer on top of
contiguous memory, rather than a generalized abstraction for any indexed
data source.
But is it just "*treating*" multi-dimensional access as a layer on top of
contiguous memory, interface-wise; or does mdspan internally *implement*
multi-dimensional access as raw-pointer operations, in order to reduce code
bloat and to reduce pressure on the compiler's optimizer? I fear mdspan
does the latter.
So I fear that your proposal is basically asking implementors to implement
(certain parts of) mdspan twice: one time for contiguous iterators, which
can be lowered to raw pointers; and a second time (non-existent today, but
forced by your proposal) for random-access-but-non-contiguous iterators,
which will be forbidden to use any clever optimizations.
I'm not necessarily right about that fear; and even if implementors *are*
forced to implement two codepaths, there's certainly precedent in the STL
for that.
But basically this indicates that your D4173R0 is conspicuously missing an
"Implementation experience" section.
Nit: "Lastest" should be "Latest."
Bikeshed: "__cpp_lib_iterator_accessor" should perhaps be
"__cpp_lib_mdspan_iterator_accessor" for clarity.
Pre-existing nit: Your *Mandates* element has the words "is required to
be"; that should be just "is". Do you feel like submitting an editorial PR
for the pre-existing misuses of "is required to be" in [mdspan.accessor]?
You write:
> -3- Each specialization of iterator_accessor is a trivially copyable type
that models semiregular.
Is this "trivially copyable" implementable? *If iterator_accessor<I>
contains an I data member*, it's not. At least this should say
"iterator_accessor<I> is trivially copyable if I is trivially copyable."
Or, alternatively, add "*Mandates:* I is trivially copyable."
The latter would be really annoying IMHO. However, it brings me back to my
first fear: Is there a *reason* that all accessors are currently specified
to be trivially copyable? Does that help the implementor somewhere later
on, that we can copy (a contiguous array of) accessors with memcpy without
knowing their exact type? If so, then permitting `iterator_accessor<I>` to
be non-trivially copyable is going to be an implementation burden. So
again, I want to see an "Implementation Experience" section and I want to
see it deal with these (imagined/feared) optimization issues head-on.
Alternatively, *if iterator_accessor<I> does not contain an I data member,*
then why stop at "trivially copyable"? Why not specify that
iterator_accessor<I> is always an *empty* type? (And then, pre-existing,
why do none of the other accessors bother to specify that? What data
members are they imagined to have, then?)
Other than the big fear that this will turn out to be unimplementable for
some technical reason, IMHO this sounds great. Again, those four Examples
are very motivating.
HTH,
Arthur
On Wed, Apr 1, 2026 at 10:01 AM Hewill Kang via Std-Proposals <
std-proposals_at_[hidden]> wrote:
> Hi, I wrote a paper on this: https://isocpp.org/files/papers/P4173R0.html
> Thanks.
>
> Hewill Kang <hewillk_at_[hidden]> 於 2026年3月30日週一 下午8:33寫道:
>
>> Hi all,
>>
>> Currently, although `mdspan` is designed as a general-purpose multiview,
>> it mostly accepts a pointer and accesses different elements through a
>> pointer algorithm.
>> However, I am not satisfied with these *pointer*-based mandates because
>> I do not think they are much different from a general
>> `contiguous_iterator`, which can be a common iterator such as
>> `vector::iterator`.
>> If we look at the `default_accessor`, we can see the clue:
>>
>> ```cpp
>> template<class ElementType>
>> struct default_accessor {
>> using offset_policy = default_accessor;
>> using element_type = ElementType;
>> using reference = ElementType&;
>> using data_handle_type = ElementType*;
>>
>> constexpr default_accessor() noexcept = default;
>> constexpr reference access(data_handle_type p, size_t i) const
>> noexcept
>> { return p[i]; }
>> constexpr data_handle_type offset(data_handle_type p, size_t i) const
>> noexcept
>> { return p + i; }
>> };
>> ```
>> Then, upon closer inspection, we discovered that this is *exactly* the
>> operation supported by C++20 `random_access_iterator`, i.e.,
>> `operator[]` and `operator+`.
>> Given this, I think we can introduce a new accessor class that wraps the
>> `random_access_iterator`, for example:
>>
>> ```cpp
>> template<random_access_iterator I>
>> struct *iter_accessor* {
>> using offset_policy = iter_accessor;
>> using reference = iter_reference_t<I>;
>> using element_type = remove_reference_t<reference>;
>> using data_handle_type = I;
>>
>> iter_accessor() noexcept = default;
>> constexpr reference
>> access(data_handle_type p, std::iter_difference_t<I> i) const
>> { return p[i]; }
>> constexpr data_handle_type
>> offset(data_handle_type p, std::iter_difference_t<I> i) const
>> { return p + i; }
>> };
>> ```
>>
>> This fully inherits the natural characteristics of a
>> `random_access_iterator`, which allows us to very easily make `mdspan`s
>> with a wide variety of different `random_access_range`s, such as:
>>
>> ```
>> using Layout = std::layout_right::mapping<std::extents<int, 3, 3>>;
>>
>> auto r = std::views::iota(0, 9);
>> auto ms1 = std::mdspan(r.begin(), Layout{}, *iter_accessor*
>> <decltype(r.begin())>{});
>> /* 0 1 2
>> 3 4 5
>> 6 7 8 */
>>
>> auto v = std::vector<bool>{true, false, true, false, true, false,
>> true, false, true};
>> auto ms2 = std::mdspan(v.begin(), Layout{}, *iter_accessor*
>> <decltype(v.begin())>{});
>> /* true false true
>> false true false
>> true false true */
>>
>> std::vector<int> v1{1, 2, 3}, v2{4, 5}, v3{};
>> std::array a{6, 7, 8};
>> auto s = std::views::single(9);
>> auto r3 = std::views::concat(v1, v2, v3, a, s);
>> auto ms3 = std::mdspan(r3.begin(), Layout{}, *iter_accessor*
>> <decltype(r3.begin())>{});
>> /* 1 2 3
>> 4 5 6
>> 7 8 9 */
>> ```
>> Demo: https://godbolt.org/z/xb5vrejba
>>
>> I think this is a very useful utility.
>> Based on C++26, we have a large majority of range adaptors in `<ranges>`
>> that can support `random_access_range`.
>> Introducing this `accessor` makes them better integrated with `mdspan`,
>> which also opens the door for third-party custom range types.
>>
>> What do you think? Appreciate any feedback.
>>
>> Hewill
>>
>>
>>
>>
>> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
Received on 2026-04-01 14:49:34
