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.
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::sentinelsynopsis, 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_)};
}
}
[…]
};
[…]
}