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-06-13
Priority: 2
View all issues with New status.
Discussion:
to_input_view was recently added to the working draft by P3137R3.
#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.
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.
to_input_view.
[2025-06-13; Reflector poll]
Set priority to 2 after reflector poll.
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_;