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.

### 3264. `sized_range` and `ranges::size` redundantly use `disable_sized_range`

Section: 26.4.3 [range.sized] Status: C++20 Submitter: Casey Carter Opened: 2019-08-26 Last modified: 2021-02-25

Priority: 1

View all other issues in [range.sized].

View all issues with C++20 status.

Discussion:

`disable_sized_range` (26.4.3 [range.sized]) is an opt-out trait that users may specialize when their `range` type conforms to the syntax of `sized_range` but not its semantics, or when the type is so poorly suited to the Standard Library that even testing the validity of the expressions `r.size()` or `size(r)` for a range `r` is impossible. The library inspects `disable_sized_range` in two places. (1) In the definition of the `sized_range` concept:

>     template<class T>
>       concept sized_range =
>         range<T> &&
>         !disable_sized_range<remove_cvref_t<T>> &&
>         requires(T& t) { ranges::size(t); };

If the pertinent specialization of `disable_sized_range` is `true`, we avoid checking the validity of the expression `ranges::size(t)` in the requires-expression. (2) In the definition of the `ranges::size` CPO itself (26.3.10 [range.prim.size]), the validity of the expressions `decay-copy(E.size())` and `decay-copy(size(E))` is not checked if the pertinent specialization of `disable_sized_range` is `true`.

`disable_sized_range` is effectively checked twice when evaluating `sized_range`. This redundancy could be forgiven, if it did not permit the existence of non-`sized_range`s for which `ranges::size` returns a valid size:

>     struct mytype {};
>     using A = mytype[42];

>     template <>
>     constexpr bool std::ranges::disable_sized_range<A> = true;

>     static_assert(std::ranges::range<A>);
>     static_assert(!std::ranges::sized_range<A>);
>     static_assert(std::ranges::size(A{}) == 42);

>     struct myrange {
>         constexpr int* begin() const { return nullptr; }
>         constexpr int* end() const { return nullptr; }
>     };

>     template <>
>     constexpr bool std::ranges::disable_sized_range<myrange> = true;

>     static_assert(std::ranges::range<myrange>);
>     static_assert(!std::ranges::sized_range<myrange>);
>     static_assert(std::ranges::size(myrange{}) == 0);

We should remove this gap between `ranges::size` and `sized_range` by checking `disable_sized_range` only in the definition of `ranges::size`, and continuing to rely on the validity of `ranges::size` in the `sized_range` concept.

[2019-09-14 Priority set to 1 based on reflector discussion]

[2019-11 Wednesday night issue processing in Belfast.]

Status to Ready

Proposed resolution:

This wording is relative to N4830.

1. Modify 26.4.3 [range.sized] as follows:

   >     template<class T>
   >       concept sized_range =
   >         range<T> &&
   >         !disable_sized_range<remove_cvref_t<T>> &&
   >         requires(T& t) { ranges::size(t); };