This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++23 status.
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.
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.
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_)}; } } […] }; […] }