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.

### 3737. `take_view::sentinel` should provide `operator-`

Section: 26.7.10.3 [range.take.sentinel] Status: WP Submitter: Hewill Kang Opened: 2022-07-15 Last modified: 2022-11-17

Priority: 3

View all other issues in [range.take.sentinel].

View all issues with WP status.

Discussion:

This issue is part of NB comment US 47-109 26 [ranges] Resolve open issues

When the underlying range is not a `sized_range`, the `begin` and `end` functions of `take_view` return `counted_iterator` and `take_view::sentinel` respectively. However, the sentinel type of the underlying range may still model `sized_sentinel_for` for its iterator type, and since `take_view::sentinel` can only be compared to `counted_iterator`, this makes `take_view` no longer able to compute the distance between its iterator and sentinel.

We are needlessly losing functionality here. Since calculating the distance, in this case, is still simple, i.e. we just need to compute the minimum of `counted_iterator::count` and the difference between the underlying iterator and sentinel, I think providing `operator-` for `take_view::sentinel` does bring some value.

[2022-08-23; Reflector poll]

Set priority to 3 after reflector poll.

Some P0 votes, but with objections: "This seems like a) a feature not a bug - of fairly limited utility?, and b) I’d like to see an implementation (maybe it’s in MSVC?) to be sure there isn’t a negative interaction we’re not thinking of."

Previous resolution [SUPERSEDED]:

> This wording is relative to N4910.

> 1. Modify 26.7.10.3 [range.take.sentinel], class template `take_view::sentinel` synopsis, as indicated:

>    > >     namespace std::ranges {
>    > >       template<view V>
>    > >       template<bool Const>
>    > >       class take_view<V>::sentinel {
>    > >       private:
>    > >         using Base = maybe-const<Const, V>;                                     // exposition only
>    > >         template<bool OtherConst>
>    > >           using CI = counted_iterator<iterator_t<maybe-const<OtherConst, V>>>;  // exposition only
>    > >         sentinel_t<Base> end_ = sentinel_t<Base>();                             // exposition only
>    > >       public:
>    > >         […]
>    > >         friend constexpr bool operator==(const CI<Const>& y, const sentinel& x);

>    > >         template<bool OtherConst = !Const>
>    > >           requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
>    > >         friend constexpr bool operator==(const CI<OtherConst>& y, const sentinel& x);

>    > >         friend constexpr range_difference_t<Base>
>    > >           operator-(const sentinel& x, const CI<Const>& y)
>    > >             requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;

>    > >         template<bool OtherConst = !Const>
>    > >           requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
>    > >         friend constexpr range_difference_t<maybe-const<OtherConst, V>>
>    > >           operator-(const sentinel& x, const CI<OtherConst>& y);

>    > >         friend constexpr range_difference_t<Base>
>    > >           operator-(const CI<Const>& x, const sentinel& y)
>    > >             requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;

>    > >         template<bool OtherConst = !Const>
>    > >           requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
>    > >         friend constexpr range_difference_t<maybe-const<OtherConst, V>>
>    > >           operator-(const CI<OtherConst>& x, const sentinel& y);
>    > >       };
>    > >     }

>    > […]

>    >     friend constexpr bool operator==(const CI<Const>& y, const sentinel& x);

>    >     template<bool OtherConst = !Const>
>    >       requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
>    >     friend constexpr bool operator==(const CI<OtherConst>& y, const sentinel& x);

>    > > -4- Effects: Equivalent to: `return y.count() == 0 || y.base() == x.end_;`

>    >     friend constexpr range_difference_t<Base>
>    >       operator-(const sentinel& x, const CI<Const>& y)
>    >         requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;

>    >     template<bool OtherConst = !Const>
>    >       requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
>    >     friend constexpr range_difference_t<maybe-const<OtherConst, V>>
>    >       operator-(const sentinel& x, const CI<OtherConst>& y);

>    > > -?- Effects: Equivalent to: `return ranges::min(y.count(), x.end_ - y.base());`

>    >     friend constexpr range_difference_t<Base>
>    >       operator-(const CI<Const>& x, const sentinel& y)
>    >         requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;

>    >     template<bool OtherConst = !Const>
>    >       requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
>    >     friend constexpr range_difference_t<maybe-const<OtherConst, V>>
>    >       operator-(const CI<OtherConst>& x, const sentinel& y);

>    > > -?- Effects: Equivalent to: `return -(y - x);`

[Kona 2022-11-08; Discussed at joint LWG/SG9 session. Move to Open]

[2022-11-09 Tim updates wording following LWG discussion]

This case is only possible if the source view is not a `sized_range`, yet its iterator/sentinel types model `sized_sentinel_for` (typically when source is an input range). In such a case we should just have `begin` compute the correct size so that `end` can just return `default_sentinel`.

[Kona 2022-11-10; Move to Immediate]

[2022-11-12 Approved at November 2022 meeting in Kona. Status changed: Immediate → WP.]

Proposed resolution:

This wording is relative to N4917.

1. Modify 26.7.10.2 [range.take.view], class template `take_view` synopsis, as indicated:

   >     namespace std::ranges {
   >       template<view V>
   >       class take_view : public view_interface<take_view<V>> {
   >       private:
   >         […]
   >       public:
   >         […]
   >         constexpr auto begin() requires (!simple-view<V>) {
   >           if constexpr (sized_range<V>) {
   >             if constexpr (random_access_range<V>) {
   >               return ranges::begin(base_);
   >             } else {
   >               auto sz = range_difference_t<V>(size());
   >               return counted_iterator(ranges::begin(base_), sz);
   >             }
   >           } else if constexpr (sized_sentinel_for<sentinel_t<V>, iterator_t<V>>) {
   >             auto it = ranges::begin(base_);
   >             auto sz = std::min(count_, ranges::end(base_) - it);
   >             return counted_iterator(std::move(it), sz);
   >           } else {
   >             return counted_iterator(ranges::begin(base_), count_);
   >           }
   >         }

   >         constexpr auto begin() const requires range<const V> {
   >           if constexpr (sized_range<const V>) {
   >             if constexpr (random_access_range<const V>) {
   >               return ranges::begin(base_);
   >             } else {
   >               auto sz = range_difference_t<const V>(size());
   >               return counted_iterator(ranges::begin(base_), sz);
   >             }
   >           } else if constexpr (sized_sentinel_for<sentinel_t<const V>, iterator_t<const V>>) {
   >             auto it = ranges::begin(base_);
   >             auto sz = std::min(count_, ranges::end(base_) - it);
   >             return counted_iterator(std::move(it), sz);
   >           } else {
   >             return counted_iterator(ranges::begin(base_), count_);
   >           }
   >         }

   >         constexpr auto end() requires (!simple-view<V>) {
   >           if constexpr (sized_range<V>) {
   >             if constexpr (random_access_range<V>)
   >               return ranges::begin(base_) + range_difference_t<V>(size());
   >             else
   >               return default_sentinel;
   >           } else if constexpr (sized_sentinel_for<sentinel_t<V>, iterator_t<V>>) {
   >             return default_sentinel;
   >           } else {
   >             return sentinel<false>{ranges::end(base_)};
   >           }
   >         }

   >         constexpr auto end() const requires range<const V> {
   >           if constexpr (sized_range<const V>) {
   >             if constexpr (random_access_range<const V>)
   >               return ranges::begin(base_) + range_difference_t<const V>(size());
   >             else
   >               return default_sentinel;
   >           } else if constexpr (sized_sentinel_for<sentinel_t<const V>, iterator_t<const V>>) {
   >             return default_sentinel;
   >           } else {
   >             return sentinel<true>{ranges::end(base_)};
   >           }
   >         }

   >         […]
   >       };
   >       […]
   >     }