C++ Logo

std-proposals

Advanced search

Re: [std-proposals] <ranges>: Provide member empty() for ranges adaptors (whenever possible)

From: Hewill Kang <hewillk_at_[hidden]>
Date: Wed, 31 Jan 2024 01:48:42 +0800
>
> But in this case my intuition was correct: `ranges::empty(r)` doesn't
> require `r` to be forward_range or sized_range. It correctly works on
> anything with a `.empty()` member.


Exactly, but that's where I think there's an issue with the standards.
If ranges::empty(r) calls (ranges::size(r) == 0) or ranges::begin(r) ==
ranges::end(r), then its return value is meaningful because it indicates
whether the range is empty, given that sized_range requires ranges::size() to
be equal to ranges::distance().
When ranges::empty(r) ultimately results in calling a range's empty()
memebr (if
it exists), what is the meaning of the value returned by its member empty()?
I don't see *anything* stating this in the standard, which means the
standard doesn't necessarily require that it returns the number of elements
in the range.
It is possible that the behavior of empty() is to clear all contents of the
range and return true if the clearing is successful, just like clear(),
which is what [[nodiscard]] is used to detect the API name that is
considered to be “not quite right API name”.
Also, the standard does not specify the time complexity of ranges::empty(),
which means that unlike rangs::size() when sized_range is satisfied, it
does *not* guarantee a constant time complexity.
This is why I want to introduce concepts such as *empty-checkable-range* to
clarify the above confusion.

So the proposal is to make sure that every Ranges view type with a
> `.size()` also has a `.empty()`, is that correct (now)?


The proposal is to provide empty() members for *input* range adapters
whenever possible. (there is no need to provide empty() for forward range
adaptors because view_interface can always synthesize it).
Given that checking whether adaptors are empty is simpler than calculating
their size, this proposal observes whether the underlying range can be
applied to ranges::empty() to synthesize the empty() member (with the
*corresponding formula* with ranges::empty(*base_*)) for the adaptors.
The inference of such a formula also results in some adaptors that do not
have a .size() member can have .empty(), such as split_view, which is
always non-empty (if LWG 4017 <https://cplusplus.github.io/LWG/issue4017>
is adopted), so it can provide an empty() that always returns false.
Hope that clarifies your question.

Hewill


Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]> 於 2024年1月31日 週三 上午1:14寫道:

> On Tue, Jan 30, 2024 at 11:45 AM Hewill Kang <hewillk_at_[hidden]> wrote:
>
>> I support the general direction of making the Ranges view types more
>>> consistent; but why is your proposal not simply to change
>>> https://eel.is/c++draft/view.interface
>>> like this?
>>
>>
>> I believe this does not solve the original issue.
>> Let’s leave aside the issue of constraints relying on themselves this may
>> cause.
>>
>
> `ranges::empty(derived())` is not the same thing as
> `ranges::empty(*this)`... oh, I see, I meant the equivalent of
> `ranges::empty(*base()*)` — that is, if the *actual thing* provides
> `empty()` then we should use that. But view_interface is CRTP — not an
> adaptor *over* the actual thing but a substrate *contributing to* the
> actual thing. My suggestion doesn't apply, then.
>
>
>
>> In my original example, the type of s | std::views::as_rvalue is as_rvalue_view<subrange<istream_iterator<int>,
>> istream_iterator<int>>>, so derived() returns as_rvalue_view, and
>> ranges::empty cannot be applied to this as_rvalue_view since it is
>> neither forward_range nor sized_range.
>>
>
> But in this case my intuition was correct: `ranges::empty(r)` doesn't
> require `r` to be forward_range or sized_range. It correctly works on
> anything with a `.empty()` member.
> https://eel.is/c++draft/range.prim.empty
>
> So the proposal is to make sure that every Ranges view type with a
> `.size()` also has a `.empty()`, is that correct (now)?
> And the only viable way to do that is to add the `.empty()` method to the
> specification of each individual view (take_view, drop_view, etc.), just
> like we already do for `.size()` today?
>
> –Arthur
>

Received on 2024-01-30 17:48:55