iota_view::iterator::operator- is overconstrained
Hewill Kang

Created on 2023-01-06.00:00:00 last changed 14 months ago


Date: 2023-02-01.20:48:23

Proposed resolution:

This wording is relative to N4917.

  1. Modify [range.iota.iterator] as indicated:

    namespace std::ranges {
      template<weakly_incrementable W, semiregular Bound>
        requires weakly-equality-comparable-with<W, Bound> && copyable<W>
      struct iota_view<W, Bound>::iterator {
        W value_ = W();             // exposition only
        friend constexpr iterator operator-(iterator i, difference_type n)
          requires advanceable<W>;
        friend constexpr difference_type operator-(const iterator& x, const iterator& y)
          requires advanceable<W> || sized_sentinel_for<W, W>;
    friend constexpr difference_type operator-(const iterator& x, const iterator& y)
      requires advanceable<W> || sized_sentinel_for<W, W>;

    -23- Effects: Equivalent to:

      using D = difference_type;
      if constexpr (is-integer-like<W>) {
        if constexpr (is-signed-integer-like<W>)
          return D(D(x.value_) - D(y.value_));
          return (y.value_ > x.value_)
            ? D(-D(y.value_ - x.value_))
            : D(x.value_ - y.value_);
      } else {
        return x.value_ - y.value_;

Date: 2023-02-15.00:00:00

[ 2023-02-01; Reflector poll ]

Set priority to 3 after reflector poll. Several P0 votes, but an objection to P0 on the basis that we don't define what it means to use sized_sentinel_for on non-iterators. Others responded that we don't need to, as we only use it with iterators, and do not intend it to be usable with anything else.

Date: 2023-01-06.00:00:00

Currently, two iota_view::iterators can be subtracted only when the underlying W type models advanceable, where advanceable consists of a series of syntactic and semantic requirements similar to the random_access_iterator concept.

However, when W is an C++20 iterator type, whether it provides subtraction is irrelevant to its iterator category. In such cases, still requiring W to support a series of random access iterator-like operations seems too restrictive. Consider:

    #include <list>
    #include <ranges>

    int main() {
      std::list l{1, 2, 3, 4, 5};
      auto it = std::counted_iterator(l.begin(), l.size());
      auto r = std::views::iota(it, std::next(it, 3));
      auto sz = r.size();           // 3 as expected
      auto d = r.end() - r.begin(); // error: no match for 'operator-'

We can get the correct size of iota_view by subtracting two counted_iterators, but we cannot subtract two iota_view::iterators to get their difference, even though the underlying counted_iterator already models sized_sentinel_for for itself, which is not satisfactory.

I think we should relax the constraints of iota_view::iterator::operator- to allow the above case, which also makes it compatible with iota_view::sentinel::operator-.

Date User Action Args
2023-02-01 20:48:23adminsetmessages: + msg13248
2023-01-06 14:59:13adminsetmessages: + msg13179
2023-01-06 00:00:00admincreate