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 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::take
denotes a range adaptor object (25.7.2 [range.adaptor.object]). LetE
andF
be expressions, letT
beremove_cvref_t<decltype((E))>
, and letD
berange_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
T
is a specialization ofempty_view
(25.6.2.2 [range.empty.view]), then((void)F, decay-copy(E))
, except that the evaluations ofE
andF
are indeterminately sequenced.(2.2) — Otherwise, if
T
modelsrandom_access_range
andsized_range
and 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 thatE
is evaluated only once, whereU
is a type determined as follows:
(2.2.1) — if
T
is a specialization ofspan
, thenU
isspan<typename T::element_type>
;(2.2.2) — otherwise, if
T
is a specialization ofbasic_string_view
, thenU
isT
;(2.2.3) — otherwise,
T
is a specialization ofsubrange
, andU
issubrange<iterator_t<T>>
;(2.3) — otherwise, if
T
is a specialization ofiota_view
(25.6.4.2 [range.iota.view]) that modelsrandom_access_range
andsized_range
, theniota_view(*ranges::begin(E), *(ranges::begin(E) + std::min<D>(ranges::distance(E), F)))
, except thatE
is evaluated only once.(2.?) — Otherwise, if
T
is a specialization ofiota_view
that modelsrandom_access_range
andsame_as<sentinel_t<T>, unreachable_sentinel_t>
istrue
, thenviews::iota(*ranges::begin(E), *(ranges::begin(E) + static_cast<D>(F)))
, except thatE
is evaluated only once.(2.4) — Otherwise, if
T
is a specialization ofrepeat_view
(25.6.5.2 [range.repeat.view]):
(2.4.1) — if
T
modelssized_range
, thenviews::repeat(*E.value_, std::min<D>(ranges::distance(E), F))except thatE
is 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::drop
denotes a range adaptor object (25.7.2 [range.adaptor.object]). LetE
andF
be expressions, letT
beremove_cvref_t<decltype((E))>
, and letD
berange_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
T
is a specialization ofempty_view
(25.6.2.2 [range.empty.view]), then((void)F, decay-copy(E))
, except that the evaluations ofE
andF
are indeterminately sequenced.(2.2) — Otherwise, if
T
modelsrandom_access_range
andsized_range
and 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::StoreSize
isfalse
,then
U(ranges::begin(E) + std::min<D>(ranges::distance(E), F), ranges::end(E))
, except thatE
is evaluated only once, whereU
isspan<typename T::element_type>
ifT
is a specialization ofspan
andT
otherwise.(2.?) — Otherwise, if
T
is a specialization ofiota_view
that modelsrandom_access_range
andsame_as<sentinel_t<T>, unreachable_sentinel_t>
istrue
, thenviews::iota(*(ranges::begin(E) + static_cast<D>(F)))
.(2.3) — Otherwise, if
T
is a specialization ofsubrange
(25.5.4 [range.subrange]) that modelsrandom_access_range
andsized_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 thatE
andF
are each evaluated only once.(2.4) — Otherwise, if
T
is a specialization ofrepeat_view
(25.6.5.2 [range.repeat.view]):
(2.4.1) — if
T
modelssized_range
, thenviews::repeat(*E.value_, ranges::distance(E) - std::min<D>(ranges::distance(E), F))except thatE
is evaluated only once;(2.4.2) — otherwise,
((void)F, decay-copy(E))
, except that the evaluations ofE
andF
are indeterminately sequenced.(2.5) — Otherwise,
drop_view(E, F)
.