libstdc++: questions about P3663

Mark Hoemmen mark.hoemmen@gmail.com
Sun Nov 2 20:41:31 GMT 2025


On Sun, Nov 2, 2025 at 6:51 AM Luc Grosheintz <luc.grosheintz@gmail.com> wrote:
>
> Hi Mark,
>
> thank you for your detailed response!
>
> On 10/31/25 7:46 PM, Mark Hoemmen wrote:
> >
> >> 1. What are the requirements for user provided integer-like objects?
> >
> > The reference implementation copies slices all over the place.  I
> > think it's fair to assume that slice specifiers are copyable and
> > movable.
>
> The context is that for libstdc++ we've been careful to not require
> move or copy ctors for mapping::operator(), or in `extents`. This
> means we've been very careful to immediately convert them to
> index_type, via `static_cast<index_type>(std::move(i))`.
>
> Therefore, I wanted to double check the following (8.15.2):
>
> auto [...slices] = submdspan_canonicalize_slices(src, raw_slices...);
>
> this copies `raw_slices`, while later in during canonicalization
> `std::move` is used very carefully. Allowing strange types like:
>
>    struct RValueInt {
>      operator int()&& {
>        return 42;
>      }
>    };
>
> i.e. object that can convert to `int` only via an r-value reference.
>
> As for Tomasz point: "it's not explicit and anything for which
> submdspan_canonicalize_slices is well-formed is valid". That's
> true and I'm not suggesting to change the code. However, I thought
> it would make sense to understand which ctors are intended/implied.
> (It helps checking for typos.)
>
> >
> >> 2. What happens if the standard adds a new kind of *canonical* selection?
> >
> > The idea is that it's never a breaking change to go from "ill-formed"
> > (status quo after adopting P3663) to "well-formed and well-defined"
> > (after hypothetical addition of a new kind of selection).
>
> Thank you, that's also how I understood it. Thanks for confirming.
> >
> >> 3. Is there a way to `static_assert` the mandate ...?
> >
> > If you're referring to [mdspan.sub.helpers] 6, in the specification of
> > _`canonical-slice`_, then the way that you implement the Mandates is
> > by means of the code in [mdspan.sub.helpers] 7.  That code will be
> > ill-formed otherwise.
>
> No, I mean the one for non-canonical slice types. Tomasz confirmed
> it's not static assertable. I just wanted to make sure that it's
> really not possible and not intended to be statically assertable.
> (There's always the risk that of: not possible "by me".)
>
> >
> > We do it that way because you can't write an expression (to use in a
> > `static_assert` or the definition of a concept) to express that a
> > structured binding declaration is well-formed.
>
> Offtopic: Is there's a good intuitive reason why it's impossible
> to check if a structured binding is well-formed?
>
> >
> >> 4. The notion of "unit-stride slice" [...]
> >
> > I debated that with Tomasz (CC'd) and lost ; - ) [...]
>
> In his reply he makes the point again. I don't feel to strongly
> either way. Personally, I think it would be nice to only define
> the idea: "canonical unit-stride slice". The advantage is that
> if I read that term I correctly guess the definition. Whereas
> if I read "unit-stride slice" my brain tries to be "clever" and
> find the other slices with unit-stride; and hence as a result
> guesses incorrectly how it's defined.
>
> >
> >> 5. [...] the question is what is the "lower bound"?
> >
> > [mdspan.sub.overview] 8.2 defines the lower bound for this case as the
> > offset `n`.  The upper bound is also `n` for this case.
>
> I'm sorry I can't find it:
>
>    * In N5014 the first time the string "lower" appears after
>      [mdspan.sub.overview] is lower_bound (think bisection).
>
>    * In P3663, the term is string "lower" appears exactly
>      twice, neither defines it. Both are: "lower bound of
>      the submdspan slice range"; and the slice range is
>      defined as an interval.
>
> The term is perfectly clear if the interval is non-empty.
> However for empty intervals, I wanted to point out that
> one possible way of reading P3663 is that's "lower bound"
> isn't obviously n if the interval is `[n,n)`.
>
> It's really just double checking some pedantry. If the
> two of you think it's perfectly clear, then there's nothing
> left to discuss :)

I think it's clear : - ) .  I appreciate you checking the wording, though!

mfh

> >> Is `pair{n, n}` a valid slice for dimension `i`?
> >
> > Yes, it is. [...]
> >
> >> `pair{n+1, n+1}` would be valid, but not `pair{cw<n+1>, cw<n+1>}`
> >
> > That's a good point.  `pair{cw<n+1>, cw<n+1>}` would have a
> > canonicalization that is invalid per [mdspan.sub.overview] 9.1.1.
> >
> > We should file an LWG issue about this.  Would you like to do it?
>
> You mean a LWG to clarify the wording of P3663, such that
> pair{n+1, n+1} is invalid?
>
> I'm happy to help, though personally, I thought that the
> formulation using first_ and last_ along with the condition:
>
>    0 <= first_ <= last_ <= n
>
> was simple and clear. Also made it immediately obvious that
> pair<size_t, size_t>{5, 3} is invalid. (I think it still is
> invalid but the reason is less easy to check, IMO.)
>
> The easiest way seems to repeat the block (8.4.9.1) for
> (8.4.10).
>
>    (10.4) - if S is a specialization of strided_slice:
>    (10.4.1) + if s.offset <= x
>    (10.4.2) + if s.extent <= x
>    (10.4.3) + if s.offset + s.extent <= x
>
> (where x is the k-th extent of `e`.) I think (10.4.2) is
> redundant and it feels clumsy because it overlaps with:
>
>    (10.2) the k-th interval of e contains the submdspan slice
>     range of s for the kth extent of e;
>
> Alternatively, something like:
>    (10.4) - if the slice range of s is empty then:
>    (10.4.1-3) as above.
>
> >
> >> 6. I'd like to suggest adding "canonical" to "valid submdspan slice":
> > [...]
> > Saying "valid canonical" would incorrectly imply that something can be
> > valid but not canonical.
>
> Understood, and that makes sense. Intuitively, I think of the two
> terms "valid" and "canonical" as orthogonal. Something can be
> canonical, but not valid and the other way around.
>
> Consider, `exts = extents(3, 5)` then both
>    auto s1 = pair{1, 3};
>    int s2 = 2;
>
> are *intutively* valid (as in they can be passed to `submdspan`),
> non-canonical, slices of the `k-th` extent (k = 0, 1).
> For example the term could be defined as:
>
>    `s` is a valid slice of the k-th extent if
>    canonical-slice<IndexType>(s) is a valid canonical slice
>    of the k-th exent.
>
> Then the precondition for submdspan_mapping is:
>
>    For each k, slices...[k] is a valid canonical slice for the k-th
>    extent.
>
> Whereas submdspan and submdspan_canonocial_slices can simply write:
>
>    For each k, slices...[k] is a valid slice for the k-th extent.
>
> I don't have a strong preference. Therefore, if the argument wasn't
> convincing, I'm happy to not "protest" further.
>


More information about the Libstdc++ mailing list