Title
take_view::sentinel should provide operator-
Status
c++23
Section
[range.take.sentinel]
Submitter
Hewill Kang

Created on 2022-07-15.00:00:00 last changed 13 months ago

Messages

Date: 2022-11-17.00:42:33

Proposed resolution:

This wording is relative to N4917.

  1. Modify [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_)};
          }
        }
        
        […]
      };
      […]
    }
    
Date: 2022-11-12.00:00:00

[ 2022-11-12 Approved at November 2022 meeting in Kona. Status changed: Immediate → WP. ]

Date: 2022-11-11.21:08:08

[ Kona 2022-11-10; Move to Immediate ]

Date: 2022-11-09.00:00:00

[ 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.

Date: 2022-11-10.23:33:20

[ Kona 2022-11-08; Discussed at joint LWG/SG9 session. Move to Open ]

Date: 2022-08-15.00:00:00

[ 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.

  1. Modify [range.take.sentinel], class template take_view::sentinel synopsis, 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);

Date: 2022-11-11.21:08:08

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.

We are needlessly losing functionality here. Since calculating the distance, in this case, is still simple, i.e. we just need to compute the minimum of counted_iterator::count and the difference between the underlying iterator and sentinel, I think providing operator- for take_view::sentinel does bring some value.

History
Date User Action Args
2023-11-22 15:47:43adminsetstatus: wp -> c++23
2022-11-17 00:42:33adminsetmessages: + msg13090
2022-11-17 00:42:33adminsetstatus: immediate -> wp
2022-11-11 21:08:08adminsetmessages: + msg13025
2022-11-11 21:08:08adminsetstatus: open -> immediate
2022-11-10 23:33:20adminsetmessages: + msg13003
2022-11-10 23:33:20adminsetstatus: new -> open
2022-11-10 03:17:58adminsetmessages: + msg12988
2022-08-23 15:25:16adminsetmessages: + msg12702
2022-07-16 16:16:39adminsetmessages: + msg12590
2022-07-15 00:00:00admincreate