This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++20 status.

### 3103. Errors in taking subview of `span` should be ill-formed where possible

Section: 22.7.3.4 [span.sub] Status: C++20 Submitter: Tomasz Kamiński Opened: 2018-04-13 Last modified: 2021-02-25

Priority: 3

View all issues with C++20 status.

Discussion:

Currently all out-of-bound/inputs errors in the functions taking an subview of `span` lead to undefined behavior, even in the situation when they could be detected at compile time. This is inconsistent with the behavior of the `span` constructors, which make similar constructs ill-formed.

Furthermore, with the current specification of the `subspan` function, the following invocation:

>     span<T, N> s;   // N > 0
>     s.subspan<O>(); // with O > 0

is ill-formed when `O > N + 1`, as the return of the function is `span<T, K>` with `K < -1`. However in case when `O == N + 1`, runtime sized `span` is returned (`span<T, -1>`) instead and the behavior of the function is undefined.

Firstly, for either run time sized (`N == dynamic_extent`) and fixed sized (`N > 0`) object `s` of type `span<T, N>`, the following constructs should be ill-formed, instead of having undefined behavior:

1. `s.first<C>()` with `C < 0`

2. `s.last<C>()` with `C < 0`

3. `s.subspan<O, E>` with `O < 0` or `E < 0` and `E != dynamic_extent`.

This would follow `span` specification, that make instantiation of `span<T, N>` ill-formed for `N < 0` and `N != dynamic_extent`.

In addition the following constructs should be made ill-formed for fixed size span `s` of type `span<T, N>` (with `N > 0`):

1. `s.first<C>()` with `C > N`

2. `s.last<C>()` with `C > N`

3. `s.subspan<O, dynamic_extent>()` with `O > N`

4. `s.subspan<O, C>()` with `O + C > N`

This will match the `span` constructor that made construction of fixed size `span<T, N>` from fixed size `span` of different size ill-formed.

[2018-04-24 Priority set to 3 after discussion on the reflector.]

[2018-11 San Diego Thursday night issue processing]

Tomasz to provide updated wording.

Previous resolution: [SUPERSEDED]

> This wording is relative to N4741.

> > 1. Edit 22.7.3.4 [span.sub] as indicated:

> >    >     template<ptrdiff_t Count> constexpr span<element_type, Count> first() const;

> >    > > -?- Remarks: If `Count < 0 || (Extent != dynamic_extent && Count > Extent)`, the program is ill-formed.

> >    > > -1- Requires: `0 <= Count && Count <= size()`.

> >    > > -2- Effects: Equivalent to: `return {data(), Count};`

> >    >     template<ptrdiff_t Count> constexpr span<element_type, Count> last() const;

> >    > > -?- Remarks: If `Count < 0 || (Extent != dynamic_extent && Count > Extent)`, the program is ill-formed.

> >    > > -3- Requires: `0 <= Count && Count <= size()`.

> >    > > -4- Effects: Equivalent to: `return {data() + (size() - Count), Count};`

> >    >     template<ptrdiff_t Offset, ptrdiff_t Count = dynamic_extent>
> >    >       constexpr span<element_type, see below> subspan() const;

> >    > > -?- Remarks: The program is ill-formed if:

> >    > > - `Offset < 0 || (Count < 0 && Count != dynamic_extent)`, or

> >    > > - `Extend != dynamic_extent && (Offset > Extent || (Count != dynamic_extent && Offset + Count > Extent))`.

> >    > > -5- Requires: `(0 <= Offset && Offset <= size()) && (Count == dynamic_extent || Count >= 0 && Offset + Count <= size())`.

> >    > > -6- Effects: Equivalent to: `return span<ElementType, see below>( data() + Offset, Count != dynamic_extent ? Count : size() - Offset);`

> >    > > -7- Remarks: The second template argument of the returned `span` type is:

> >    > > >     Count != dynamic_extent ? Count
> >    > > >                             : (Extent != dynamic_extent ? Extent - Offset
> >    > > >                                                         : dynamic_extent)

[2018-11-09; Tomasz provides updated wording]

I have decided to replace all Requires: elements in the section 22.7.3.4 [span.sub] to preserve consistency.

Previous resolution: [SUPERSEDED]

> This wording is relative to N4778.

> > 1. Edit 22.7.3.4 [span.sub] as indicated:

> >    >     template<ptrdiff_t Count> constexpr span<element_type, Count> first() const;

> >    > > -?- Mandates: `Count >= 0 && (Extent == dynamic_extent || Count <= Extent)`.

> >    > > -1- Requires Expects: `0 <= Count && Count <= size()`.

> >    > > -2- Effects: Equivalent to: `return {data(), Count};`

> >    >     template<ptrdiff_t Count> constexpr span<element_type, Count> last() const;

> >    > > -?- Mandates: `Count >= 0 && (Extent == dynamic_extent || Count <= Extent)`.

> >    > > -3- Requires Expects: `0 <= Count && Count <= size()`.

> >    > > -4- Effects: Equivalent to: `return {data() + (size() - Count), Count};`

> >    >     template<ptrdiff_t Offset, ptrdiff_t Count = dynamic_extent>
> >    >       constexpr span<element_type, see below> subspan() const;

> >    > > -?- Mandates: `Offset >= 0 && (Count >= 0 || Count == dynamic_extent) && (Extent == dynamic_extent || (Offset <= Extent && (Count == dynamic_extent || Offset + Count <= Extent)))`.

> >    > > -5- Requires Expects: `(0 <= Offset && Offset <= size()) && (Count == dynamic_extent || Count >= 0 && Offset + Count <= size())`.

> >    > > -6- Effects: Equivalent to: `return span<ElementType, see below>( data() + Offset, Count != dynamic_extent ? Count : size() - Offset);`

> >    > > -7- Remarks: The second template argument of the returned `span` type is:

> >    > > >     Count != dynamic_extent ? Count
> >    > > >                             : (Extent != dynamic_extent ? Extent - Offset
> >    > > >                                                         : dynamic_extent)

> >    >     constexpr span<element_type, dynamic_extent> first(index_type count) const;

> >    > > -8- Requires Expects: `0 <= count && count <= size()`.

> >    > > -9- Effects: Equivalent to: `return {data(), count};`

> >    >     constexpr span<element_type, dynamic_extent> last(index_type count) const;

> >    > > -10- Requires Expects: `0 <= count && count <= size()`.

> >    > > -11- Effects: Equivalent to: `return {data() + (size() - count), count};`

> >    >     constexpr span<element_type, dynamic_extent> subspan(
> >    >       index_type offset, index_type count = dynamic_extent) const;

> >    > > -12- Requires Expects: `(0 <= offset && offset <= size()) && (count == dynamic_extent || count >= 0 && offset + count <= size())`

> >    > > -13- Effects: Equivalent to: `return {data() + offset, count == dynamic_extent ? size() - offset : count};`

[2019-06-23; Tomasz comments and provides updated wording]

The current proposed resolution no longer applies to the newest revision of the standard (N4820), due changes introduced in P1227 (making `size()` and template parameters of `span` unsigned).

[2019 Cologne Wednesday night]

Status to Ready

Proposed resolution:

This wording is relative to N4820.

> [Drafting note: This wording relies on observation, that the condition in form `Extent == dynamic_extent || Count <= Extent`, can be simplified into `Count <= Extent`, because `dynamic_extent` is equal to `numeric_limits<size_t>::max()`, thus `size() <= Extent` is always true, and `Extent == dynamic_extent` implies that `Count <= Extent`.

> Furthermore we check that `Count != dynamic_extent || Count <= Extent - Offset`, as the `Offset + Count <= Extent` may overflow (defined for unsigned integers) and produce false positive result. This change is also applied to Expects clause. ]

> 1. Edit 22.7.3.4 [span.sub] as indicated:

>    >     template<size_t Count> constexpr span<element_type, Count> first() const;

>    > > -?- Mandates: `Count <= Extent` is `true`.

>    > > -1- Expects: `Count <= size()` is `true`.

>    > > -2- Effects: Equivalent to: `return {data(), Count};`

>    >     template<size_t Count> constexpr span<element_type, Count> last() const;

>    > > -?- Mandates: `Count <= Extent` is `true`.

>    > > -3- Expects: `Count <= size()` is `true`.

>    > > -4- Effects: Equivalent to: `return {data() + (size() - Count), Count};`

>    >     template<size_t Offset, size_t Count = dynamic_extent>
>    >       constexpr span<element_type, see below> subspan() const;

>    > > -?- Mandates: `Offset <= Extent && (Count == dynamic_extent || Count <= Extent - Offset)` is `true`.

>    > > -5- Expects: `Offset <= size() && (Count == dynamic_extent || Offset + Count <= size()Count <= size() - Offset)` is `true`.

>    > > -6- Effects: Equivalent to: `return span<ElementType, see below>(data() + Offset, Count != dynamic_extent ? Count : size() - Offset);`

>    > > -7- Remarks: The second template argument of the returned `span` type is:

>    > > >     Count != dynamic_extent ? Count
>    > > >                             : (Extent != dynamic_extent ? Extent - Offset
>    > > >                                                         : dynamic_extent)

>    > […]

>    >     constexpr span<element_type, dynamic_extent> subspan(
>    >       index_type offset, index_type count = dynamic_extent) const;

>    > > -12- Expects: `offset <= size() && (count == dynamic_extent || offset + count <= size()count <= size() - offset)` is `true`.

>    > > -13- Effects: Equivalent to: `return {data() + offset, count == dynamic_extent ? size() - offset : count};`