This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.
to_input_view::iterator
cannot be compared to its const
sentinelSection: 25.7.35 [range.to.input] Status: New Submitter: Hewill Kang Opened: 2025-03-15 Last modified: 2025-03-22
Priority: Not Prioritized
View all issues with New status.
Discussion:
to_input_view
was recently added to the working draft by P3137R3.
Consider: (demo):
#include <ranges> int main() { auto r = std::views::single(0) | std::views::chunk(1) | std::views::to_input; r.begin() == std::as_const(r).end(); // #1, error r.begin() == r.cend(); // #2, error }
In #1
, r.begin()
returns to_input_view<chunk_view>::iterator<false>
,
while the latter returns chunk_view::iterator<true>
.
Since the former can only be compared with chunk_view::iterator<false>
that cannot
be converted from chunk_view::iterator<true>
, the two are incomparable.
This can be fixed by adding the following overload to to_input_view::iterator
:
template<bool OtherConst = !Const> requires sentinel_for<sentinel_t<maybe-const<OtherConst, V>>, iterator_t<Base>> friend constexpr bool operator==(const iterator& x, const sentinel_t<maybe-const<OtherConst, V>>& y) { return x.current_ == y; }
Unfortunately, it still doesn't resolve #2
, because r.cend()
returns
basic_const_iterator<chunk_view::iterator<true>>
, which cannot be compared to any
non-copyable iterators as its operator==(const S& s)
requires S
to be a
sentinel type, which rules out to_input_view::iterator
, so the constraint is not satisfied.
I believe we may need to introduce a custom sentinel class for to_input_view
.
Proposed resolution:
This wording is relative to N5008.
Modify 25.7.35.2 [range.to.input.view] as indicated:
[…]namespace std::ranges { template<input_range V> requires view<V> class to_input_view : public view_interface<to_input_view<V>> { V base_ = V(); // exposition only template<bool Const> class iterator; // exposition only template<bool Const> class sentinel; // exposition only […] }; […] }constexpr auto end() requires (!simple-view<V>);-?- Effects: Equivalent to:
return sentinel<false>(ranges::end(base_));constexpr auto end() const requires range<const V>;-4- Effects: Equivalent to:
return sentinel<true>(ranges::end(base_));
Modify 25.7.35.3 [range.to.input.iterator] as indicated:
[…]namespace std::ranges { template<input_range V> requires view<V> template<bool Const> class to_input_view<V>::iterator { […]friend constexpr bool operator==(const iterator& x, const sentinel_t<Base>& y); friend constexpr difference_type operator-(const sentinel_t<Base>& y, const iterator& x) requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>; friend constexpr difference_type operator-(const iterator& x, const sentinel_t<Base>& y) requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;[…] }; }friend constexpr bool operator==(const iterator& x, const sentinel_t<Base>& y);
-7- Returns:x.current_ == y
.friend constexpr difference_type operator-(const sentinel_t<Base>& y, const iterator& x) requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
-8- Returns:y - x.current_
.friend constexpr difference_type operator-(const iterator& x, const sentinel_t<Base>& y) requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
-9- Returns:x.current_ - y
.
Add [range.to.input.sentinel] after [range.to.input.iterator] as indicated:
namespace std::ranges { template<input_range V> requires view<V> template<bool Const> class to_input_view<V>::sentinel { using Base = maybe-const<Const, V>; // exposition only sentinel_t<Base> end_ = sentinel_t<Base>(); // exposition only constexpr explicit sentinel(sentinel_t<Base> end); // exposition only public: sentinel() = default; constexpr sentinel(sentinel<!Const> other) requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>; constexpr sentinel_t<Base> base() const; template<bool OtherConst> requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y); template<bool OtherConst> requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr range_difference_t<maybe-const<OtherConst, V>> operator-(const iterator<OtherConst>& x, const sentinel& y); template<bool OtherConst> 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 iterator<OtherConst>& y); }; }constexpr explicit sentinel(sentinel_t<Base> end);-?- Effects: Initializes
end_
withstd::move(end)
.constexpr sentinel(sentinel<!Const> other) requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;-?- Effects: Initializes
end_
withstd::move(other.end_)
.constexpr sentinel_t<Base> base() const;-?- Effects: Equivalent to:
return end_;
template<bool OtherConst> requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y);-?- Effects: Equivalent to:
return x.current_ == y.end_;
template<bool OtherConst> requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr range_difference_t<maybe-const<OtherConst, V>> operator-(const iterator<OtherConst>& x, const sentinel& y);-?- Effects: Equivalent to:
return x.current_ - y.end_;
template<bool OtherConst> 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 iterator<OtherConst>& y);-?- Effects: Equivalent to:
return x.end_ - y.current_;