This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++23 status.
Section: 25.7.10.1 [range.take.overview], 25.6.4 [range.iota] Status: C++23 Submitter: Patrick Palka Opened: 2020-02-21 Last modified: 2023-11-22
Priority: 2
View other active issues in [range.take.overview].
View all other issues in [range.take.overview].
View all issues with C++23 status.
Discussion:
Section 6.1 of P1739R4 changes the specification of
views::take as follows:
-2- The name
views::takedenotes a range adaptor object (25.7.2 [range.adaptor.object]).Given subexpressionsLetEandF, the expressionviews::take(E, F)is expression-equivalent totake_view{E, F}.EandFbe 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:
— if
Tis a specialization ofranges::empty_view(25.6.2.2 [range.empty.view]), then((void) F, decay-copy(E));— otherwise, if
Tmodelsrandom_access_rangeandsized_rangeand is
— a specialization of
span(23.7.2.2 [views.span]) whereT::extent == dynamic_extent,— a specialization of
basic_string_view(27.3 [string.view]),— a specialization of
ranges::iota_view(25.6.4.2 [range.iota.view]), or— a specialization of
ranges::subrange(25.5.4 [range.subrange]),then
T{ranges::begin(E), ranges::begin(E) + min<D>(ranges::size(E), F)}, except thatEis evaluated only once;— otherwise,
ranges::take_view{E, F}.
Consider the case when T = subrange<counted_iterator<int>, default_sentinel_t>.
Then according to the above wording, views::take(E, F) is expression-equivalent to
T{ranges::begin(E), ranges:begin(E) + min<D>(ranges::size(E), F)}; (*)
But this expression is ill-formed for the T we chose because
subrange<counted_iterator<int>, default_sentinel_t> has no matching
constructor that takes an iterator-iterator pair.
T is a specialization of
subrange that does not model common_range. But a similar issue also
exists when T is a specialization of iota_view whose value type differs
from its bound type. In this case yet another issue arises: In order for the expression
(*) to be well-formed when T is a specialization of iota_view,
we need to be able to construct an iota_view out of an iterator-iterator pair,
and for that it seems we need to add another constructor to iota_view.
[2020-02-24, Casey comments]
Furthermore, the pertinent subrange constructor is only available when subrange::StoreSize is
false (i.e., when either the subrange specialization's third template argument is
not subrange_kind::sized or its iterator and sentinel types I and S model
sized_sentinel_for<S, I>).
[2020-03-16, Tomasz comments]
A similar problem occurs for the views::drop for the subrange<I, S, subrange_kind::sized>,
that explicitly stores size (i.e. sized_sentinel_for<I, S> is false). In such case,
the (iterator, sentinel) constructor that views::drop will be expression-equivalent is
not available.
[2020-03-29 Issue Prioritization]
Priority to 2 after reflector discussion.
[2021-05-18 Tim adds wording]
The proposed resolution below is based on the MSVC implementation, with one caveat:
the MSVC implementation uses a SCARY iterator type for iota_view and therefore
its iota_view case for take is able to directly construct the new view
from the iterator type of the original. This is not required to work, so the wording below
constructs the iota_view from the result of dereferencing the iterators instead
in this case.
[2021-06-18 Tim syncs wording to the current working draft]
The wording below also corrects the size calculation in the presence of integer-class types.
[2021-08-20; LWG telecon]
Set status to Tentatively Ready after telecon review.
[2021-10-14 Approved at October 2021 virtual plenary. Status changed: Voting → WP.]
Proposed resolution:
This wording is relative to N4892.
Edit 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 ofranges::empty_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_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 thatEis evaluated only once, whereUis a type determined as follows:
(2.2.1) — if
Tis a specialization ofspan(23.7.2.2 [views.span]) where, thenT::extent == dynamic_extentUisspan<typename T::element_type>;(2.2.2) — otherwise, if
Tis a specialization ofbasic_string_view(27.3 [string.view]), thenUisT;
(2.2.3) — a specialization ofranges::iota_view(25.6.4.2 [range.iota.view]), or(2.2.4) — otherwise,
Tis a specialization ofranges::subrange(25.5.4 [range.subrange]), andUisranges::subrange<iterator_t<T>>;
thenT{ranges::begin(E), ranges::begin(E) + min<D>(ranges::size(E), F)}, except thatEis evaluated only once;(2.?) — otherwise, if
Tis a specialization ofranges::iota_view(25.6.4.2 [range.iota.view]) that modelsrandom_access_rangeandsized_range, thenranges::iota_view(*ranges::begin(E), *(ranges::begin(E) + std::min<D>(ranges::distance(E), F))), except thatEandFare each evaluated only once;(2.3) — otherwise,
ranges::take_view(E, F).
Edit 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 ofranges::empty_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_range, and is
(2.2.1) —a specialization of
span(23.7.2.2 [views.span])where,T::extent == dynamic_extent(2.2.2) — a specialization of
basic_string_view(27.3 [string.view]),(2.2.3) — a specialization of
ranges::iota_view(25.6.4.2 [range.iota.view]), or(2.2.4) — a specialization of
ranges::subrange(25.5.4 [range.subrange]) whereT::StoreSizeisfalse,then
, except thatTU(ranges::begin(E) + std::min<D>(ranges::sizedistance(E), F), ranges::end(E))Eis evaluated only once, whereUisspan<typename T::element_type>ifTis a specialization ofspanandTotherwise;(2.?) — otherwise, if
Tis a specialization ofranges::subrange(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.3) — otherwise,
ranges::drop_view(E, F).