take_view::sentinel
should provide operator-
Section: 25.7.10.3 [range.take.sentinel] Status: C++23 Submitter: Hewill Kang Opened: 2022-07-15 Last modified: 2023-11-22
Priority: 3
View all other issues in [range.take.sentinel].
View all issues with C++23 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.
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.
Modify 25.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.
Modify 25.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_)}; } } […] }; […] }