Title
sized_range and ranges::size redundantly use disable_sized_range
Status
c++20
Section
[range.sized]
Submitter
Casey Carter

Created on 2019-08-26.00:00:00 last changed 45 months ago

Messages

Date: 2019-11-07.08:57:59

Proposed resolution:

This wording is relative to N4830.

  1. Modify [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); };
    
Date: 2019-11-07.08:57:59

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

Status to Ready

Date: 2019-09-14.00:00:00

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

Date: 2019-08-26.00:00:00

disable_sized_range ([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 ([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_ranges 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.

History
Date User Action Args
2021-02-25 10:48:01adminsetstatus: wp -> c++20
2020-02-24 16:02:59adminsetstatus: voting -> wp
2020-01-17 04:54:50adminsetstatus: ready -> voting
2019-11-07 08:57:59adminsetmessages: + msg10788
2019-11-07 08:57:59adminsetstatus: new -> ready
2019-09-14 16:47:29adminsetmessages: + msg10615
2019-08-27 01:14:45adminsetmessages: + msg10584
2019-08-26 00:00:00admincreate