Title
Some problems with the wording changes of P1739R4
Status
ready
Section
[range.take.overview][range.iota]
Submitter
Patrick Palka

Created on 2020-02-21.00:00:00 last changed 1 month ago

Messages

Date: 2021-08-20.17:21:40

Proposed resolution:

This wording is relative to N4892.

  1. Edit [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:

    1. (2.1) — if T is a specialization of ranges::empty_view ([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 ([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:

      1. (2.2.1) — if T is a specialization of span ([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 ([string.view]), then U is T;

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

      4. (2.2.4) — otherwise, T is a specialization of ranges::subrange ([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 ([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 [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:

    1. (2.1) — if T is a specialization of ranges::empty_view ([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 ([views.span]) where T::extent == dynamic_extent,

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

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

      4. (2.2.4) — a specialization of ranges::subrange ([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 ([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).

Date: 2021-08-15.00:00:00

[ 2021-08-20; LWG telecon ]

Set status to Tentatively Ready after telecon review.

Date: 2021-06-18.00:00:00

[ 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.

Date: 2021-05-18.00:00:00

[ 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.

Date: 2020-03-29.00:00:00

[ 2020-03-29 Issue Prioritization ]

Priority to 2 after reflector discussion.

Date: 2020-03-15.00:00:00

[ 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.

Date: 2020-02-15.00:00:00

[ 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>).

Date: 2020-02-21.00:00:00

Section 6.1 of P1739R4 changes the specification of views::take as follows:

-2- The name views::take denotes a range adaptor object ([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 ([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 ([views.span]) where T::extent == dynamic_extent,

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

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

    4. — a specialization of ranges::subrange ([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.

History
Date User Action Args
2021-08-20 17:21:40adminsetmessages: + msg12011
2021-08-20 17:21:40adminsetstatus: new -> ready
2021-06-19 18:11:20adminsetmessages: + msg11944
2021-05-19 03:41:19adminsetmessages: + msg11818
2021-05-19 03:41:19adminsetmessages: + msg11817
2020-03-29 15:38:28adminsetmessages: + msg11177
2020-03-21 11:46:21adminsetmessages: + msg11169
2020-02-24 20:22:21adminsetmessages: + msg11149
2020-02-21 00:00:00admincreate