This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of WP status.

### 3407. Some problems with the wording changes of P1739R4

Section: 24.7.8.1 [range.take.overview], 24.6.4 [range.iota] Status: WP Submitter: Patrick Palka Opened: 2020-02-21 Last modified: 2021-10-14

Priority: 2

View all issues with WP 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 (24.7.2 [range.adaptor.object]). Given subexpressions `E` and `F`, the expression `views::take(E, F)` is expression-equivalent to `take_view{E, F}`. 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:

> 1. — if `T` is a specialization of `ranges::empty_view` (24.6.2.2 [range.empty.view]), then `((void) F, decay-copy(E))`;

> 2. — otherwise, if `T` models `random_access_range` and `sized_range` and is

>    1. — a specialization of `span` (22.7.3 [views.span]) where `T::extent == dynamic_extent`,

>    2. — a specialization of `basic_string_view` (21.4 [string.view]),

>    3. — a specialization of `ranges::iota_view` (24.6.4.2 [range.iota.view]), or

>    4. — a specialization of `ranges::subrange` (24.5.4 [range.subrange]),

>    then `T{ranges::begin(E), ranges::begin(E) + min<D>(ranges::size(E), F)}`, except that `E` is evaluated only once;

> 3. — 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.

More generally the above issue applies anytime `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.

1. Edit 24.7.8.1 [range.take.overview] as indicated:

   > -2- The name `views::take` denotes a range adaptor object (24.7.2 [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:

   > 1. (2.1) — if `T` is a specialization of `ranges::empty_view` (24.6.2.2 [range.empty.view]), then `((void) F, decay-copy(E))`, except that the evaluations of `E` and `F` are indeterminately sequenced;

   > 2. (2.2) — otherwise, if `T` models `random_access_range` and `sized_range`, and is a specialization of `span` (22.7.3 [views.span]), `basic_string_view` (21.4 [string.view]), or `ranges::subrange` (24.5.4 [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:

   >    1. (2.2.1) — if `T` is a specialization of `span` (22.7.3 [views.span]) where `T::extent == dynamic_extent`, then `U` is `span<typename T::element_type>`;

   >    2. (2.2.2) — otherwise, if `T` is a specialization of `basic_string_view` (21.4 [string.view]), then `U` is `T`;

   >    3. (2.2.3) — a specialization of `ranges::iota_view` (24.6.4.2 [range.iota.view]), or

   >    4. (2.2.4) — otherwise, `T` is a specialization of `ranges::subrange` (24.5.4 [range.subrange]), and `U` is `ranges::subrange<iterator_t<T>>`;

   >    then `T{ranges::begin(E), ranges::begin(E) + min<D>(ranges::size(E), F)}`, except that `E` is evaluated only once;

   > 3. (2.?) — otherwise, if `T` is a specialization of `ranges::iota_view` (24.6.4.2 [range.iota.view]) that models `random_access_range` and `sized_range`, then `ranges::iota_view(*ranges::begin(E), *(ranges::begin(E) + std::min<D>(ranges::distance(E), F)))`, except that `E` and `F` are each evaluated only once;

   > 4. (2.3) — otherwise, `ranges::take_view(E, F)`.

2. Edit 24.7.10.1 [range.drop.overview] as indicated:

   > -2- The name `views::drop` denotes a range adaptor object (24.7.2 [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:

   > 1. (2.1) — if `T` is a specialization of `ranges::empty_view` (24.6.2.2 [range.empty.view]), then `((void) F, decay-copy(E))`, except that the evaluations of `E` and `F` are indeterminately sequenced;

   > 2. (2.2) — otherwise, if `T` models `random_access_range` and `sized_range`, and is

   >    1. (2.2.1) —a specialization of `span` (22.7.3 [views.span]) where `T::extent == dynamic_extent`,

   >    2. (2.2.2) — a specialization of `basic_string_view` (21.4 [string.view]),

   >    3. (2.2.3) — a specialization of `ranges::iota_view` (24.6.4.2 [range.iota.view]), or

   >    4. (2.2.4) — a specialization of `ranges::subrange` (24.5.4 [range.subrange]) where `T::StoreSize` is `false`,

   >    then `TU(ranges::begin(E) + std::min<D>(ranges::sizedistance(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;

   > 3. (2.?) — otherwise, if `T` is a specialization of `ranges::subrange` (24.5.4 [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;

   > 4. (2.3) — otherwise, `ranges::drop_view(E, F)`.