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.

4490. Allow calling std::ranges::size in ranges algorithms

Section: 26.2 [algorithms.requirements] Status: New Submitter: Ruslan Arutyunyan Opened: 2025-12-12 Last modified: 2025-12-20

Priority: Not Prioritized

View other active issues in [algorithms.requirements].

View all other issues in [algorithms.requirements].

View all issues with New status.

Discussion:

26.2 [algorithms.requirements] paragraph 14 says that the algorithms in ranges namespace are implemented as if they are dispatched to the corresponding overload with iterator and sentinel. However, there are two problems with the current wording:

  1. We only allow to call either std::ranges::end(r) or std::ranges::next(std::ranges::begin(r), std::ranges::end()) to calculate a corresponding sentinel. However, this is a pessimization for some ranges because we can have sized ranges without sized_sentinel_for. Consider the following example:

    const char str[] = "something";
    ranges::subrange<const char*,
                    null_sentinel_t,
                    subrange_kind::sized> sr(ranges::begin(str),
                                              null_sentinel,
                                              ranges::size(str) - 1);
    my::ranges::next(sr.begin(), sr.end()); // this line serially calculates iterator that is equal to sr.end()
    

    Despite the fact that we know the size of the range and that the range is the random access one, std::ranges::next calculates the iterator serially, because it only has constant time complexity optimization for sized_sentinel_for. We could clearly achieve better complexity with a different API or with the different overload of the same API.

  2. It is unclear when an algorithm calls std::ranges::end(r) and when it calls std::ranges::next(std::ranges::begin(r), std::ranges::end()) to calculate the corresponding sentinel because there is no established priority between them. Maybe, it's smaller problem but still it's worth clarifying in my opinion.

Proposed resolution:

This wording is relative to N5032.

  1. Modify 26.2 [algorithms.requirements] as indicated:

    […]

    -14- Overloads of algorithms that take range arguments (25.4.2 [range.range]) behave as if they are implemented by dispatching to the overload in namespace ranges that takes separate iterator and sentinel arguments, where for each range argument r

    • (14.1) — a corresponding iterator argument is initialized with ranges::begin(r) and

    • (14.2) — a corresponding sentinel argument is initialized with ranges::end(r), or ranges::next(ranges::begin(r), ranges::end(r)) if the type of r models forward_range and computing ranges::next meets the specified complexity requirements.one of the following:

      • (14.2.1) — if the type of r models forward_range and computing ranges::next meets the specified complexity requirements then

        • (14.2.1.1) — ranges::next(ranges::begin(r), ranges::size(r)) if r models sized_range, otherwise

        • (14.2.1.2) — ranges::next(ranges::begin(r), ranges::end(r)).

      • (14.2.2) — Otherwise, std::ranges::end(s).