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.

I don't think that, currently, disallowing the creation of a view with ranges::to is a good direction.
So I wrote P3544 ranges::to view , which is supported by SG9 as far as I know.

Have you considered just adding nonmember functions that create an
mdspan with the desired properties, such as the following?
 
I think adding a `from_range` constructor to `mspan` is worthwhile. It provides at least two enhancements that I find quite nice: 

1. It can perform compile-time and runtime size validation, which pointer-based currently cannot do:
  array<int, 12> arr{};
  auto m1 = mdspan(from_range, arr, cw<3>, cw<4>);   // ok
  auto m2 = mdspan(from_range, arr, cw<4>, cw<4>);   // compile-time reject

  vector<float> v(15);
  auto m3 = mdspan(from_range, v, 3, 5);             // ok
  auto m4 = mdspan(from_range, v, 4, 5);             // runtime assert
This is feasible because for integral-constant-like types of extents, the mapping's `required_span_size()` becomes a constant expression.

2. It integrates well with `ranges::to`, as the latter natively supports `from_range_t tag-dispatched` constructors according to [range.utility.conv.to].
  auto grid_10x10 = views::iota(0) | ranges::to<mdspan>(10, 10);

  vector x_coords = {0, 10, 20};
  vector y_coords = {0, 5};
  vector z_coords = {0, 100};
  // Represents a 3D grid of coordinate tuples: (x, y, z)
  auto coord_grid = views::cartesian_product(x_coords, y_coords, z_coords);
  auto points = coord_grid | ranges::to<mdspan>(x_coords.size(), 
                                                y_coords.size(), 
                                                z_coords.size());

// Physical buffer: Data is interleaved or padded (e.g., 128-bit alignment)
vector<float> hardware_buffer = { /* large padded data */ };
auto mat_4x4 = hardware_buffer | views::stride(4) | views::as_const | ranges::to<mdspan>(4, 4);
I think this spelling might be more fascinating because we only focus on the parameter of dimension.

Thanks,
Hewill

Mark Hoemmen <mark.hoemmen@gmail.com> 於 2026年4月6日週一 下午12:28寫道:
On Sat, Apr 4, 2026 at 10:28 AM Hewill Kang <hewillk@gmail.com> 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@gmail.com> 於 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@gmail.com> 於 2026年4月4日週六 上午11:54寫道:
>>>
>>> Thank you, that makes sense.
>>> I updated the paper.
>>>
>>>
>>>
>>>
>>> Mark Hoemmen <mark.hoemmen@gmail.com> 於 2026年4月4日週六 上午3:16寫道:
>>>>
>>>> On Fri, Apr 3, 2026 at 11:27 AM Hewill Kang <hewillk@gmail.com> 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@proton.me> 於 2026年4月3日週五 上午2:48寫道:
>>>> >>
>>>> >> On Thursday, April 2nd, 2026 at 8:24 PM, Mark Hoemmen via Std-Proposals <std-proposals@lists.isocpp.org> 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.