23 Ranges library [ranges]

23.6 Range utilities [range.utility]

23.6.3 Sub-ranges [range.subrange]

The subrange class template combines together an iterator and a sentinel into a single object that models the View concept.
Additionally, it models the SizedRange concept when the final template parameter is subrange_­kind::sized.
namespace std::ranges {
  template<class T>
    concept pair-like =                                 // exposition only
      !is_reference_v<T> && requires(T t) {
        typename tuple_size<T>::type;   // ensures tuple_­size<T> is complete
        requires DerivedFrom<tuple_size<T>, integral_constant<size_t, 2>>;
        typename tuple_element_t<0, remove_const_t<T>>;
        typename tuple_element_t<1, remove_const_t<T>>;
        { get<0>(t) } -> const tuple_element_t<0, T>&;
        { get<1>(t) } -> const tuple_element_t<1, T>&;
      };

  template<class T, class U, class V>
    concept pair-like-convertible-to =                  // exposition only
      !Range<T> && pair-like<remove_reference_t<T>> &&
      requires(T&& t) {
        { get<0>(std::forward<T>(t)) } -> ConvertibleTo<U>;
        { get<1>(std::forward<T>(t)) } -> ConvertibleTo<V>;
      };

  template<class T, class U, class V>
    concept pair-like-convertible-from =                // exposition only
      !Range<T> && pair-like<T> && Constructible<T, U, V>;

  template<class T>
    concept iterator-sentinel-pair =                    // exposition only
      !Range<T> && pair-like<T> &&
      Sentinel<tuple_element_t<1, T>, tuple_element_t<0, T>>;

  template<Iterator I, Sentinel<I> S = I, subrange_kind K =
      SizedSentinel<S, I> ? subrange_kind::sized : subrange_kind::unsized>
    requires (K == subrange_kind::sized || !SizedSentinel<S, I>)
  class subrange : public view_interface<subrange<I, S, K>> {
  private:
    static constexpr bool StoreSize =                   // exposition only
      K == subrange_kind::sized && !SizedSentinel<S, I>;
    I begin_ = I();                                     // exposition only
    S end_ = S();                                       // exposition only
    iter_difference_t<I> size_ = 0;                     // exposition only; present only
                                                        // when StoreSize is true
  public:
    subrange() = default;

    constexpr subrange(I i, S s) requires (!StoreSize);

    constexpr subrange(I i, S s, iter_difference_t<I> n)
      requires (K == subrange_kind::sized);

    template<not-same-as<subrange> R>
      requires forwarding-range<R> &&
        ConvertibleTo<iterator_t<R>, I> && ConvertibleTo<sentinel_t<R>, S>
    constexpr subrange(R&& r) requires (!StoreSize || SizedRange<R>);

    template<forwarding-range R>
      requires ConvertibleTo<iterator_t<R>, I> && ConvertibleTo<sentinel_t<R>, S>
    constexpr subrange(R&& r, iter_difference_t<I> n)
      requires (K == subrange_kind::sized)
        : subrange{ranges::begin(r), ranges::end(r), n}
    {}

    template<not-same-as<subrange> PairLike>
      requires pair-like-convertible-to<PairLike, I, S>
    constexpr subrange(PairLike&& r) requires (!StoreSize)
      : subrange{std::get<0>(std::forward<PairLike>(r)),
                 std::get<1>(std::forward<PairLike>(r))}
    {}

    template<pair-like-convertible-to<I, S> PairLike>
    constexpr subrange(PairLike&& r, iter_difference_t<I> n)
      requires (K == subrange_kind::sized)
      : subrange{std::get<0>(std::forward<PairLike>(r)),
                 std::get<1>(std::forward<PairLike>(r)), n}
    {}

    template<not-same-as<subrange> PairLike>
      requires pair-like-convertible-from<PairLike, const I&, const S&>
    constexpr operator PairLike() const;

    constexpr I begin() const;
    constexpr S end() const;

    constexpr bool empty() const;
    constexpr iter_difference_t<I> size() const
      requires (K == subrange_kind::sized);

    [[nodiscard]] constexpr subrange next(iter_difference_t<I> n = 1) const;
    [[nodiscard]] constexpr subrange prev(iter_difference_t<I> n = 1) const
      requires BidirectionalIterator<I>;
    constexpr subrange& advance(iter_difference_t<I> n);

    friend constexpr I begin(subrange&& r) { return r.begin(); }
    friend constexpr S end(subrange&& r) { return r.end(); }
  };

  template<Iterator I, Sentinel<I> S>
    subrange(I, S, iter_difference_t<I>) -> subrange<I, S, subrange_kind::sized>;

  template<iterator-sentinel-pair P>
    subrange(P) -> subrange<tuple_element_t<0, P>, tuple_element_t<1, P>>;

  template<iterator-sentinel-pair P>
    subrange(P, iter_difference_t<tuple_element_t<0, P>>) ->
      subrange<tuple_element_t<0, P>, tuple_element_t<1, P>, subrange_kind::sized>;

  template<forwarding-range R>
    subrange(R&&) ->
      subrange<iterator_t<R>, sentinel_t<R>,
               (SizedRange<R> || SizedSentinel<sentinel_t<R>, iterator_t<R>>)
                 ? subrange_kind::sized : subrange_kind::unsized>;

  template<forwarding-range R>
    subrange(R&&, iter_difference_t<iterator_t<R>>) ->
      subrange<iterator_t<R>, sentinel_t<R>, subrange_kind::sized>;

  template<size_t N, class I, class S, subrange_kind K>
    requires (N < 2)
  constexpr auto get(const subrange<I, S, K>& r);
}

namespace std {
  using ranges::get;
}

23.6.3.1 Constructors and conversions [range.subrange.ctor]

constexpr subrange(I i, S s) requires (!StoreSize);
Effects: Initializes begin_­ with i and end_­ with s.
constexpr subrange(I i, S s, iter_difference_t<I> n) requires (K == subrange_kind::sized);
Expects: n == ranges::distance(i, s).
Effects: Initializes begin_­ with i and end_­ with s.
If StoreSize is true, initializes size_­ with n.
[Note
:
Accepting the length of the range and storing it to later return from size() enables subrange to model SizedRange even when it stores an iterator and sentinel that do not model SizedSentinel.
end note
]
template<not-same-as<subrange> R> requires forwarding-range<R> && ConvertibleTo<iterator_t<R>, I> && ConvertibleTo<sentinel_t<R>, S> constexpr subrange(R&& r) requires (!StoreSize || SizedRange<R>);
Effects: Equivalent to:
  • If StoreSize is true, subrange{ranges::begin(r), ranges::end(r), ranges::size(r)}.
  • Otherwise, subrange{ranges::begin(r), ranges::end(r)}.
template<not-same-as<subrange> PairLike> requires pair-like-convertible-from<PairLike, const I&, const S&> constexpr operator PairLike() const;
Effects: Equivalent to: return PairLike(begin_­, end_­);

23.6.3.2 Accessors [range.subrange.access]

constexpr I begin() const;
Effects: Equivalent to: return begin_­;
constexpr S end() const;
Effects: Equivalent to: return end_­;
constexpr bool empty() const;
Effects: Equivalent to: return begin_­ == end_­;
constexpr iter_difference_t<I> size() const requires (K == subrange_kind::sized);
Effects:
  • If StoreSize is true, equivalent to: return size_­;
  • Otherwise, equivalent to: return end_­ - begin_­;
[[nodiscard]] constexpr subrange next(iter_difference_t<I> n = 1) const;
Effects: Equivalent to:
auto tmp = *this;
tmp.advance(n);
return tmp;
[Note
:
If I does not model ForwardIterator, next can invalidate *this.
end note
]
[[nodiscard]] constexpr subrange prev(iter_difference_t<I> n = 1) const requires BidirectionalIterator<I>;
Effects: Equivalent to:
auto tmp = *this;
tmp.advance(-n);
return tmp;
constexpr subrange& advance(iter_difference_t<I> n);
Effects: Equivalent to:
  • If StoreSize is true,
    size_ -= n - ranges::advance(begin_, n, end_);
    return *this;
    
  • Otherwise,
    ranges::advance(begin_, n, end_);
    return *this;
    
template<size_t N, class I, class S, subrange_kind K> requires (N < 2) constexpr auto get(const subrange<I, S, K>& r);
Effects: Equivalent to:
if constexpr (N == 0)
  return r.begin();
else
  return r.end();