Title
Should views::iota(0) | views::take(5) be views::iota(0, 5)?
Status
new
Section
[range.take.overview][range.take.overview]
Submitter
Hewill Kang

Created on 2024-01-28.00:00:00 last changed 2 months ago

Messages

Date: 2024-01-28.12:54:14

Proposed resolution:

This wording is relative to N4971.

  1. Modify [range.take.overview] as indicated:

    -2- The name views::take denotes a range adaptor object ([range.adaptor.object]). Let E and F be expressions, let T be remove_cvref_t<decltype((E))>, and let D be range_difference_t<decltype((E))>. If decltype((F)) does not model convertible_to<D>, views::take(E, F) is ill-formed. Otherwise, the expression views::take(E, F) is expression-equivalent to:

    1. (2.1) — if T is a specialization of empty_view ([range.empty.view]), then ((void)F, decay-copy(E)), except that the evaluations of E and F are indeterminately sequenced.

    2. (2.2) — Otherwise, if T models random_access_range and sized_range and is a specialization of span ([views.span]), basic_string_view ([string.view]), or ranges::subrange ([range.subrange]), then U(ranges::begin(E), ranges::begin(E) + std::min<D>(ranges::distance(E), F)), except that E is evaluated only once, where U is a type determined as follows:

      1. (2.2.1) — if T is a specialization of span, then U is span<typename T::element_type>;

      2. (2.2.2) — otherwise, if T is a specialization of basic_string_view, then U is T;

      3. (2.2.3) — otherwise, T is a specialization of subrange, and U is subrange<iterator_t<T>>;

    3. (2.3) — otherwise, if T is a specialization of iota_view ([range.iota.view]) that models random_access_range and sized_range, then iota_view(*ranges::begin(E), *(ranges::begin(E) + std::min<D>(ranges::distance(E), F))), except that E is evaluated only once.

    4. (2.?) — Otherwise, if T is a specialization of iota_view that models random_access_range and same_as<sentinel_t<T>, unreachable_sentinel_t> is true, then views::iota(*ranges::begin(E), *(ranges::begin(E) + static_cast<D>(F))), except that E is evaluated only once.

    5. (2.4) — Otherwise, if T is a specialization of repeat_view ([range.repeat.view]):

      1. (2.4.1) — if T models sized_range, then

          views::repeat(*E.value_, std::min<D>(ranges::distance(E), F))
        except that E is evaluated only once;

      2. (2.4.2) — otherwise, views::repeat(*E.value_, static_cast<D>(F)).

    6. (2.5) — Otherwise, take_view(E, F).

  2. Modify [range.drop.overview] as indicated:

    -2- The name views::drop denotes a range adaptor object ([range.adaptor.object]). Let E and F be expressions, let T be remove_cvref_t<decltype((E))>, and let D be range_difference_t<decltype((E))>. If decltype((F)) does not model convertible_to<D>, views::drop(E, F) is ill-formed. Otherwise, the expression views::drop(E, F) is expression-equivalent to:

    1. (2.1) — if T is a specialization of empty_view ([range.empty.view]), then ((void)F, decay-copy(E)), except that the evaluations of E and F are indeterminately sequenced.

    2. (2.2) — Otherwise, if T models random_access_range and sized_range and is

      1. (2.2.1) — a specialization of span ([views.span]),

      2. (2.2.2) — a specialization of basic_string_view ([string.view]),

      3. (2.2.3) — a specialization of iota_view ([range.iota.view]), or

      4. (2.2.4) — a specialization of subrange ([range.subrange]) where T::StoreSize is false,

      then U(ranges::begin(E) + std::min<D>(ranges::distance(E), F), ranges::end(E)), except that E is evaluated only once, where U is span<typename T::element_type> if T is a specialization of span and T otherwise.

    3. (2.?) — Otherwise, if T is a specialization of iota_view that models random_access_range and same_as<sentinel_t<T>, unreachable_sentinel_t> is true, then views::iota(*(ranges::begin(E) + static_cast<D>(F))).

    4. (2.3) — Otherwise, if T is a specialization of subrange ([range.subrange]) that models random_access_range and sized_range, then T(ranges::begin(E) + std::min<D>(ranges::distance(E), F), ranges::end(E), to-unsigned-like(ranges::distance(E) - std::min<D>(ranges::distance(E), F))), except that E and F are each evaluated only once.

    5. (2.4) — Otherwise, if T is a specialization of repeat_view ([range.repeat.view]):

      1. (2.4.1) — if T models sized_range, then

          views::repeat(*E.value_, ranges::distance(E) - std::min<D>(ranges::distance(E), F))
        except that E is evaluated only once;

      2. (2.4.2) — otherwise, ((void)F, decay-copy(E)), except that the evaluations of E and F are indeterminately sequenced.

    6. (2.5) — Otherwise, drop_view(E, F).

Date: 2024-01-28.00:00:00

Given that C++20 ranges does not introduce the infinite range notification present in range/v3, this means that views::iota(0) | views::take(5) will currently return a take_view object that does not model sized_range.

However, with the introduction of C++23 repeat_view, its interaction with views::take/drop does have special handling depending on whether it is an infinite range, which causes views::repeat(0) | views::take(5) to return a repeat_view objects that satisfy sized_range.

This inconsistency leads to very different behavior of these two range factories in the case of infinite ranges (demo):

#include <ranges>

auto take_and_drop = std::views::drop(5)
                   | std::views::take(4)
                   | std::views::drop(3)
                   | std::views::take(2)
                   | std::views::drop(1);

// The type of iota is drop_view<take_view<drop_view<take_view<drop_view<iota_view<int, unreachable_sentinel_t>>>>>>, which is indeed a template bloat.
auto iota = std::views::iota(0) | take_and_drop;
static_assert(std::ranges::sized_range<decltype(iota)>); // failed

// The type of repeat is simply std::ranges::repeat_view<int, long>
std::ranges::sized_range auto repeat = std::views::repeat(0) | take_and_drop; // ok

If we do account for the infinity of repeat_view, then I see no reason not to do it for iota_view, as this is obviously intuitive and can indeed be considered an enhancement.

History
Date User Action Args
2024-01-28 12:54:14adminsetmessages: + msg13941
2024-01-28 00:00:00admincreate