3609. std::ranges::iota_view<int, long> has non-subtractable iterator and sentinel types

Section: 25.6.4.4 [range.iota.sentinel] Status: New Submitter: Jiang An Opened: 2021-09-25 Last modified: 2021-10-14

Priority: 3

View all issues with New status.

Discussion:

Currently std::ranges::iota_view<int, long> uses its special sentinel type, as its W and Bound are different types and Bound is not std::unreachable_sentinel_t. However, as W (int) is not an iterator type, the iterator and sentinel types don't satisfy std::sized_sentinel_for, and thus not subtractable.

IMO we should handle operator- overloads for iota_view::iterator and iota_view::sentinel like iota_view::size and operator- for iota_view::iterator.

[2021-10-14; Reflector poll]

Set priority to 3 after reflector poll.

[Tim Song commented:]

We might be able to simplify the (y_value > x.value_) conditional since we know that we are working with an iterator and its sentinel.

Proposed resolution:

This wording is relative to N4892.

  1. Modify 25.6.4.4 [range.iota.sentinel] 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>::sentinel {
      private:
        Bound bound_ = Bound(); // exposition only
      public:
        sentinel() = default;
        constexpr explicit sentinel(Bound bound);
        
        friend constexpr bool operator==(const iterator& x, const sentinel& y);
        
        friend constexpr iter_difference_t<W>IOTA-DIFF-T(W) operator-(const iterator& x, const sentinel& y)
          requires (is-integer-like<W> && is-integer-like<Bound>) || sized_sentinel_for<Bound, W>;
    
        friend constexpr iter_difference_t<W>IOTA-DIFF-T(W) operator-(const sentinel& x, const iterator& y)
          requires (is-integer-like<W> && is-integer-like<Bound>) || sized_sentinel_for<Bound, W>;
      };
    }
    

    […]

    friend constexpr iter_difference_t<W>IOTA-DIFF-T(W) operator-(const iterator& x, const sentinel& y)
      requires (is-integer-like<W> && is-integer-like<Bound>) || sized_sentinel_for<Bound, W>;
    

    -3- Effects: Equivalent to: return x.value_ - y.bound_;

    using D = IOTA-DIFF-T(W);
    if constexpr (is-integer-like<W>) {
      auto y_value = W(y.bound_);
      if constexpr (is-signed-integer-like<W>) {
        return D(D(x.value_) - D(y_value));
      } else {
        return (y_value > x.value_)
          ? D(-D(y_value - x.value_))
          : D(x.value_ - y_value);
      }
    } else {
      return x.value_ - y.bound_;
    }
    
    friend constexpr iter_difference_t<W>IOTA-DIFF-T(W) operator-(const sentinel& x, const iterator& y)
      requires (is-integer-like<W> && is-integer-like<Bound>) || sized_sentinel_for<Bound, W>;
    

    -4- Effects: Equivalent to: return -(y - x);