Created on 2024-01-28.00:00:00 last changed 9 months ago
Proposed resolution:
This wording is relative to N4971.
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:
(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) — 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:
(2.2.1) — if T is a specialization of span, then U is span<typename T::element_type>;
(2.2.2) — otherwise, if T is a specialization of basic_string_view, then U is T;
(2.2.3) — otherwise, T is a specialization of subrange, and U is subrange<iterator_t<T>>;
(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.
(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.
(2.4) — Otherwise, if T is a specialization of repeat_view ([range.repeat.view]):
(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.4.2) — otherwise, views::repeat(*E.value_, static_cast<D>(F)).
(2.5) — Otherwise, take_view(E, F).
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:
(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) — Otherwise, if T models random_access_range and sized_range and is
(2.2.1) — a specialization of span ([views.span]),
(2.2.2) — a specialization of basic_string_view ([string.view]),
(2.2.3) — a specialization of iota_view ([range.iota.view]), or
(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.
(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))).
(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.
(2.4) — Otherwise, if T is a specialization of repeat_view ([range.repeat.view]):
(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.4.2) — otherwise, ((void)F, decay-copy(E)), except that the evaluations of E and F are indeterminately sequenced.
(2.5) — Otherwise, drop_view(E, F).
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:14 | admin | set | messages: + msg13941 |
2024-01-28 00:00:00 | admin | create |