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

4062. ranges::empty has no semantic requirements for forward_ranges

Section: 25.3.13 [range.prim.empty] Status: New Submitter: Hewill Kang Opened: 2024-03-30 Last modified: 2025-10-20

Priority: 3

View all issues with New status.

Discussion:

This is a small part of issues raised by already-closed P3156.

Currently, ranges::empty (25.3.13 [range.prim.empty]) is always a valid expression when applied to forward_ranges, because even if it does not have .empty() or .size() we can always check whether it is empty by comparing its begin() and end(), which is reflected in the standard preference to use it to check whether a certain forward_range is empty, such as in the Effects of split_view::find-next() (25.7.17.2 [range.split.view]) and cartesian_product_view::end() (25.7.33.2 [range.cartesian.view]).

In addition, MSVC-STL also uses ranges::empty in the implementation of ranges::contains_subrange for the check.

However, unlike ranges::size, which has a sized_range concept to ensure semantics, ranges::empty has no corresponding one. This makes it lack of time complexity guarantees and semantics for the meaning of the returned value when using the bullets (2.2) and (2.3) of 25.3.13 [range.prim.empty] to check emptiness.

Perhaps we need to add semantic requirements for ranges::empty, but this seems inconsistent with the current wording as no other CPOs have.

Alternatively, maybe we could move bullets targeting forward_ranges to the very beginning so that ranges::empty always has correct semantics with bool(ranges::begin(t) == ranges::end(t)) when applied to forward_ranges.

[2025-10-20; Reflector poll.]

Set priority to 3 after reflector poll.

"This is the part of P3156 that is actually a defect. We need to require ranges::empty to actually mean what we think it means. Would be reasonable to add a sematic requirement to the range concept that if ranges::empty(r) is well-formed then it does what it should do (equal to bool(begin == end), has amortized constant complexity, does not modify r, is not required to be valid after begin for non-forward)."

Proposed resolution: