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 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::take
denotes a range adaptor object (25.7.2 [range.adaptor.object]).Given subexpressionsLetE
andF
, the expressionviews::take(E, F)
is expression-equivalent totake_view{E, F}
.E
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:
— if
T
is a specialization ofranges::empty_view
(25.6.2.2 [range.empty.view]), then((void) F, decay-copy(E))
;— otherwise, if
T
modelsrandom_access_range
andsized_range
and 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 thatE
is 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::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 ofranges::empty_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
(23.7.2.2 [views.span]) where, thenT::extent == dynamic_extent
U
isspan<typename T::element_type>
;(2.2.2) — otherwise, if
T
is a specialization ofbasic_string_view
(27.3 [string.view]), thenU
isT
;
(2.2.3) — a specialization ofranges::iota_view
(25.6.4.2 [range.iota.view]), or(2.2.4) — otherwise,
T
is a specialization ofranges::subrange
(25.5.4 [range.subrange]), andU
isranges::subrange<iterator_t<T>>
;
thenT{ranges::begin(E), ranges::begin(E) + min<D>(ranges::size(E), F)}
, except thatE
is evaluated only once;(2.?) — otherwise, if
T
is a specialization ofranges::iota_view
(25.6.4.2 [range.iota.view]) that modelsrandom_access_range
andsized_range
, thenranges::iota_view(*ranges::begin(E), *(ranges::begin(E) + std::min<D>(ranges::distance(E), F)))
, except thatE
andF
are 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::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 ofranges::empty_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])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::StoreSize
isfalse
,then
, except that
TU(ranges::begin(E) + std::min<D>(ranges::sizedistance(E), F), ranges::end(E))E
is evaluated only once, whereU
isspan<typename T::element_type>
ifT
is a specialization ofspan
andT
otherwise;(2.?) — otherwise, if
T
is a specialization ofranges::subrange
(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.3) — otherwise,
ranges::drop_view(E, F)
.