views::iota(0) | views::take(5) be views::iota(0, 5)?Section: 25.7.10.1 [range.take.overview], 25.7.10.1 [range.take.overview] Status: New Submitter: Hewill Kang Opened: 2024-01-28 Last modified: 2024-01-28
Priority: Not Prioritized
View other active issues in [range.take.overview].
View all other issues in [range.take.overview].
View all issues with New status.
Discussion:
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.
Proposed resolution:
This wording is relative to N4971.
Modify 25.7.10.1 [range.take.overview] as indicated:
-2- The name
views::takedenotes a range adaptor object (25.7.2 [range.adaptor.object]). LetEandFbe expressions, letTberemove_cvref_t<decltype((E))>, and letDberange_difference_t<decltype((E))>. Ifdecltype((F))does not modelconvertible_to<D>,views::take(E, F)is ill-formed. Otherwise, the expressionviews::take(E, F)is expression-equivalent to:
(2.1) — if
Tis a specialization ofempty_view(25.6.2.2 [range.empty.view]), then((void)F, decay-copy(E)), except that the evaluations ofEandFare indeterminately sequenced.(2.2) — Otherwise, if
Tmodelsrandom_access_rangeandsized_rangeand is a specialization ofspan(23.7.2.2 [views.span]),basic_string_view(27.3 [string.view]), orranges::subrange(25.5.4 [range.subrange]), thenU(ranges::begin(E), ranges::begin(E) + std::min<D>(ranges::distance(E), F)), except thatEis evaluated only once, whereUis a type determined as follows:
(2.2.1) — if
Tis a specialization ofspan, thenUisspan<typename T::element_type>;(2.2.2) — otherwise, if
Tis a specialization ofbasic_string_view, thenUisT;(2.2.3) — otherwise,
Tis a specialization ofsubrange, andUissubrange<iterator_t<T>>;(2.3) — otherwise, if
Tis a specialization ofiota_view(25.6.4.2 [range.iota.view]) that modelsrandom_access_rangeandsized_range, theniota_view(*ranges::begin(E), *(ranges::begin(E) + std::min<D>(ranges::distance(E), F))), except thatEis evaluated only once.(2.?) — Otherwise, if
Tis a specialization ofiota_viewthat modelsrandom_access_rangeandsame_as<sentinel_t<T>, unreachable_sentinel_t>istrue, thenviews::iota(*ranges::begin(E), *(ranges::begin(E) + static_cast<D>(F))), except thatEis evaluated only once.(2.4) — Otherwise, if
Tis a specialization ofrepeat_view(25.6.5.2 [range.repeat.view]):
(2.4.1) — if
Tmodelssized_range, thenviews::repeat(*E.value_, std::min<D>(ranges::distance(E), F))except thatEis evaluated only once;(2.4.2) — otherwise,
views::repeat(*E.value_, static_cast<D>(F)).(2.5) — Otherwise,
take_view(E, F).
Modify 25.7.12.1 [range.drop.overview] as indicated:
-2- The name
views::dropdenotes a range adaptor object (25.7.2 [range.adaptor.object]). LetEandFbe expressions, letTberemove_cvref_t<decltype((E))>, and letDberange_difference_t<decltype((E))>. Ifdecltype((F))does not modelconvertible_to<D>,views::drop(E, F)is ill-formed. Otherwise, the expressionviews::drop(E, F)is expression-equivalent to:
(2.1) — if
Tis a specialization ofempty_view(25.6.2.2 [range.empty.view]), then((void)F, decay-copy(E)), except that the evaluations ofEandFare indeterminately sequenced.(2.2) — Otherwise, if
Tmodelsrandom_access_rangeandsized_rangeand is
(2.2.1) — a specialization of
span(23.7.2.2 [views.span]),(2.2.2) — a specialization of
basic_string_view(27.3 [string.view]),(2.2.3) — a specialization of
iota_view(25.6.4.2 [range.iota.view]), or(2.2.4) — a specialization of
subrange(25.5.4 [range.subrange]) whereT::StoreSizeisfalse,then
U(ranges::begin(E) + std::min<D>(ranges::distance(E), F), ranges::end(E)), except thatEis evaluated only once, whereUisspan<typename T::element_type>ifTis a specialization ofspanandTotherwise.(2.?) — Otherwise, if
Tis a specialization ofiota_viewthat modelsrandom_access_rangeandsame_as<sentinel_t<T>, unreachable_sentinel_t>istrue, thenviews::iota(*(ranges::begin(E) + static_cast<D>(F))).(2.3) — Otherwise, if
Tis a specialization ofsubrange(25.5.4 [range.subrange]) that modelsrandom_access_rangeandsized_range, thenT(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 thatEandFare each evaluated only once.(2.4) — Otherwise, if
Tis a specialization ofrepeat_view(25.6.5.2 [range.repeat.view]):
(2.4.1) — if
Tmodelssized_range, thenviews::repeat(*E.value_, ranges::distance(E) - std::min<D>(ranges::distance(E), F))except thatEis evaluated only once;(2.4.2) — otherwise,
((void)F, decay-copy(E)), except that the evaluations ofEandFare indeterminately sequenced.(2.5) — Otherwise,
drop_view(E, F).