<html>

<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title><code>views::cycle</code></title>
  <style type="text/css">
    body {
      font-variant-ligatures: none;
    }

    p {
      text-align: justify
    }

    li {
      text-align: justify
    }

    blockquote.note,
    div.note {
      background-color: #E0E0E0;
      padding-left: 15px;
      padding-right: 15px;
      padding-top: 1px;
      padding-bottom: 1px;
    }

    p code {
      color: navy
    }

    ins p code {
      color: #00A000
    }

    p ins code {
      color: #00A000
    }

    p del code {
      color: #A00000
    }

    ins {
      color: #00A000
    }

    del {
      color: #A00000
    }

    table#boilerplate {
      border: 0
    }

    table#boilerplate td {
      padding-left: 2em
    }

    table.bordered,
    table.bordered th,
    table.bordered td {
      border: 1px solid;
      text-align: center;
    }

    ins.block {
      color: #00A000;
      text-decoration: none
    }

    del.block {
      color: #A00000;
      text-decoration: none
    }

    #hidedel:checked~* del,
    #hidedel:checked~* del * {
      display: none;
      visibility: hidden
    }
  </style>
</head>

<body data-new-gr-c-s-check-loaded="14.1043.0" data-gr-ext-installed="">
  <table id="boilerplate">
    <tbody>
      <tr>
        <td>Document number</td>
        <td>P3806R0</td>
      </tr>
      <tr>
        <td>Date</td>
        <td>2025-08-02</td>
      </tr>
      <tr>
        <td>Audience</td>
        <td>LEWG, SG9 (Ranges)</td>
      </tr>
      <tr>
        <td>Reply-to</td>
        <td>Hewill Kang &lt;hewillk@gmail.com&gt;</td>
      </tr>
    </tbody>
  </table>
  <hr>
  <h1><code>views::cycle</code></h1>
  <ul>
    <li>
      <ul>
        <li>Abstract</li>
        <li>Revision history</li>
        <li>Discussion</li>
        <li>Design</li>
        <li>Implementation experience</li>
        <li>Proposed change</li>
        <li>References</li>
      </ul>
    </li>
  </ul>
  <a name="Abstract"></a>
  <h2>Abstract</h2>
  <p>
    This paper proposes adding <code>views::cycle</code>, a Tier 1 range adaptor as described in <a
      href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2760r1.html">P2760</a>, to enhance the C++29
    Ranges library by enabling infinite repetition of a range's elements.
  </p>
  <a name="Revision history"></a>
  <h2>Revision history</h2>
  <h3>R0</h3>
  <p>Initial revision.
  </p>
  </ol>
  </p>
  <a name="Discussion"></a>
  <h2>Discussion</h2>
  <p>

    There is currently no standard range adaptor in C++ that allows repeating a range endlessly. Although similar
    behavior can be approximated via <code>views::repeat(r) | views::join</code> or a custom <code>generator</code>,
    such constructs are limited to forward ranges and often introduce additional complexity and boilerplate.
    Additionally, they lack the
    semantic clarity and composability offered by a dedicated adaptor.</p>
  <p>
    The ability to cycle through elements infinitely is a common requirement in many domains: circular buffers,
    animations, event loops, and more. This functionality exists natively in other modern languages (e.g., Python's
    <code>itertools.cycle</code>, Rust's <code>.cycle()</code>),
    highlighting a gap in C++'s otherwise powerful Ranges library.
  </p>
  <p>
    We propose introducing <code>views::cycle</code> as a simple, intuitive, and efficient adaptor for producing
    infinite, multi-pass ranges that cycle through the original range. This fills a notable gap in the existing standard
    and improves parity with other languages while supporting both eager and lazy range pipelines:
  </p>
  <pre>
  for (auto&& song : playlist | views::cycle | views::take(100)) {
    play(song);
  }
</pre>
  </ol>
  <a name="Design"></a>
  <h2>Design</h2>
  <h3>Class signature</h3>
  <p>Since <code>views::cycle</code> repeats the original range,
    the minimum requirement is <code>forward_range</code> to ensure multi-pass. The proposed signature of
    <code>cycle_class</code> is:
  </p>
  <pre>
  template&lt;view V&gt;
    requires forward_range&lt;V&gt;
  class cycle_view : public view_interface&lt;cycle_view&lt;V&gt;&gt {
    // ...
  };
  </pre>
  </h3>
  <h3>Handling of Empty Ranges</h3>
  <p>
    range/v3's cycle view (which is named <code>cycled_view</code>) requires the original range to be
    <a
      href="https://github.com/ericniebler/range-v3/blob/ca1388fb9da8e69314dda222dc7b139ca84e092f/include/range/v3/view/cycle.hpp#L198C9-L202C10">non-empty</a>:
  <pre>
  explicit cycled_view(Rng rng)
    : rng_(std::move(rng))
  {
      RANGES_EXPECT(!ranges::empty(rng_));
  }
</pre>
  <p>It always expects a non-empty range and produces an infinite range. This requires the user to ensure the input
    range is non-empty, or else the behavior is undefined:</p>
  </p>
  <pre>
  auto e = ranges::views::empty&lt;int&gt;;
  auto c1 = e | ranges::views::cycle;   // <i>UB</i>

  vector&lt;int&gt; v;
  auto c2 = v | ranges::views::cycle;   // <i>also UB</i>
</pre>
  <p>
    Unlike range-v3's <code>views::cycle</code>, the
    proposed <code>views::cycle</code> supports empty ranges. This is consistent with other standard range adaptors that
    naturally handle empty input by producing empty views.
    Supporting empty ranges avoids undefined behavior in common scenarios, such as default-initializing a
    <code>cycle_view</code>:
  </p>
  <pre>
  auto c = vector{42} | views::cycle;
  decltype(c) c2{};     // <i>Default construction is now well-formed</i>
</pre>
  <p>
    This improves composability by aligning with the standard library's treatment of empty ranges, yielding intuitive
    and consistent behavior. Consequently, since a <code>cycle_view</code> can now be empty, its <code>end()</code>
    cannot be
    <code>unreachable_sentinel</code> as in
    range-v3. Instead, we use
    <code>default_sentinel</code>, with the following semantics:
  </p>
  <ul>
    <li>
      If the base range is empty, <code>begin() == end()</code> and the <code>cycle_view</code> is empty.</li>
    <li>
      If the base range is non-empty, iteration proceeds infinitely, and <code>end()</code> is never reached.</li>
  </ul>
  <p>
    This approach preserves correctness for both empty and infinite cases without introducing special-case logic.</p>
  <h3>End Iterator Is Not Cached</h3>
  <p>
    For bidirectional operations such as <code>operator--()</code>, when we find that the current iterator has reached
    the beginning of the original range, we need to move the current iterator back to the last element of the original
    range:
  <pre>
  if (<i>current_</i> == ranges::begin(<i>base_</i>)) {
    <i>current_</i> = <i>end-iterator-of-base_</i>; // <i>Repositioning</i>
    // ...
  }
  --<i>current_</i>;
</pre>
  <p>
    However, getting the end iterator of the original range may not be constant time unless the original range is
    <code>common_range</code> or models both <code>sized_range</code> and <code>random_access_range</code>; in such
    case,
    we can extract them as we do in <code><i>cartesian-common-arg-end</i></code>:
  <pre>
  template&lt;<i>cartesian-product-common-arg</i> R&gt;
  constexpr auto <i>cartesian-common-arg-end</i>(R& r) {       // <i>exposition only</i>
    if constexpr (common_range&lt;R&gt;) {
      return ranges::end(r);
    } else {
      return ranges::begin(r) + ranges::distance(r);
    }
  }</pre>
  </p>
  </p>
  <p>However,
    it is worth noting that
    range/v3 takes a more aggressive approach, providing bidirectional operations as long as the underlying range models
    <code>bidirectional_range</code>, and
    providing random-access operations as long as the underlying range models <code>random_access_range</code>.
  </p>
  <p>For unsized ranges, random-access operation requires knowing the actual size to locate the correct position. In
    this case,
    range/v3's <code>cycled_view</code> will <i>eagerly</i>
    calculate the end iterator
    of the original range and <a
      href="
    https://github.com/ericniebler/range-v3/blob/ca1388fb9da8e69314dda222dc7b139ca84e092f/include/range/v3/view/cycle.hpp#L79-L87">cache</a> it
    internally, so
    that it can be used to compute the size next time:
  </p>
  <pre>
  const char* s = /* */;
  ranges::subrange rng(s,null_sentinel{});
  static_assert(!ranges::sized_range&lt;decltype(rng)&gt;);

  auto cycled = rng | ranges::views::cycle;
  auto it = cycled.begin() + 42;   // <i>O(n) time to get the rng's end iterator for computing size</i></pre>
  <p>
    Similarly, for bidirectional operations, range-v3 will also eagerly cache the end iterator if the underlying range
    is not <code>common_range</code>.
  </p>
  <p>
    Unlike range/v3, we deliberately avoid this optimization to prevent hidden costs and semantic surprises. Instead,
    we propose to restrict such operations only when the underlying range natively
    supports them, that is, when it is a <code>random_access_range</code> and <code>sized_range</code> for
    random-access, or a
    <code>bidirectional_range</code> and <code>common_range</code> for bidirectional support.
  </p>
  <p>This avoids hidden O(n)
    complexity, preserves lazy evaluation, reduces internal state, and eliminates the need for caching, consistent with
    the design philosophy of other standard views.
  </p>
  <h3>Not support <code>iter_swap</code></h3>
  <p><code>cycle_view</code> does not provide <code>iter_swap</code>, even if the underlying range is indirectly
    swappable. Although each iterator
    in a <code>cycle_view</code> can be dereferenced to access elements of the base range, those iterators may alias the
    same
    position in the base range due to cycling behavior:</p>
  <pre>
  vector v = {1, 2, 3};
  auto cycled = v | views::cycle;

  auto it1 = cycled.begin();                    // <i>points to v[0]</i>
  auto it2 = next(cycled.begin(), 3);           // <i>also points to v[0] due to cycling</i>

  iter_swap(it1, it2);
  // <i>This looks like a swap between the first and fourth elements,</i>
  // <i>but it's actually swapping v[0] with itself</i>
</pre>
  <p>Allowing <code>iter_swap</code> in such a context could lead to misleading or
    self-aliased operations, which violate user expectations and introduce semantic ambiguity. To avoid this,
    <code>cycle_view::<i>iterator</i></code> does not provide <code>iter_swap</code> specialization.
  </p>
  <h3><code>difference_type</code> Considerations</h3>
  <p>
  <p>
    To support operations such as iterator difference or positioning, <code>cycle_view::<i>iterator</i></code> needs to
    track how many full passes have been made through the base range. This is typically implemented by maintaining a
    counter <code>n</code>, which is incremented each time the
    iterator loops back to the beginning of the base range.</p>
  <p>Although <code>n</code> can be represented using the <code>difference_type</code> of the underlying range,
    since the number of cycles itself only needs to count a finite number of iterations, for
    <code>difference_type</code> of <code>cycle_view::<i>iterator</i></code>, the original <code>difference_type</code>
    may not be a good choice.
  </p>
  <p>
    The reason is that the logical distance between two <code>cycle_view::<i>iterator</i></code>s may become much larger
    than any value that can be stored in the original <code>difference_type</code>; in such cases, subtracting them will
    result in overflow which leads to UB.</p>
  <p>
    As a result, we propose using an integral-class type to represent the <code>difference_type</code> of
    <code>cycle_view::<i>iterator</i></code>, that is, an implementation-defined integer type that is sufficiently wide
    to accommodate the expected range of iteration.
  </p>
  </p>
  <h3>Not support <code>views::cycle(5)</code></h3>
  <p>We do not propose supporting an overload of <code>views::cycle</code> that takes an integral value, such as
    <code>views::cycle(5)</code>,
    as a way to repeat a value or cycle a range a fixed number of times. While superficially convenient, such a form
    introduces semantic ambiguity and conflicts with established conventions in the Ranges library.
  </p>
  <p>In particular, <code>views::cycle</code> already supports the following two idiomatic forms:</p>
  <pre>
  rng | views::cycle          // <i>Pipe syntax</i>
  views::cycle(rng)           // <i>Function-call syntax</i></pre>
  <p>
    These forms make it clear that <code>views::cycle</code> is a range adaptor closure object: it takes a range and
    returns a new view that conceptually repeats the elements of the input range infinitely.</p>
  <p>
    Introducing something like <code>views::cycle(5)</code> could lead users to misinterpret it as
    <code>views::cycle(views::single(5))</code>, i.e., an infinite repetition of a single value.
    And those who wish to repeat a range exactly <code>n</code> times can compose <code>cycle</code> and
    <code>take</code> in
    more explicit way like
    <code>views::cycle(rng) | views::take(ranges::distance(rng) * n)</code>.
  </p>
  <p>As such, this proposal deliberately omits such usage, consistent with range/v3.</p>
  </p>
  <h3>Not borrow range</h3>
  <p>
    <code>cycle_view</code> cannot be a <code>borrowed_range</code>, because its iterators internally store a pointer to
    the <code>cycle_view</code> in order to access the stored underlying range.
  </p>
  <p>Specifically, the iterator relies on calling <code>ranges::end</code> on underlying range to determine whether it
    has reached the
    end of a cycle. As such, <code>cycle_view</code> cannot be a <code>borrowed_range</code>, because the validity of
    its iterators depends on the
    lifetime of the view object.</p>
  </p>
  <a name="Implementation experience"></a>
  <h2>Implementation experience</h2>
  <p>The author implemented <code>views::cycle</code> based on libstdc++, see <a
      href="https://godbolt.org/z/3xo1vGs3f">here</a>.</p>
  <a name="Proposed-change"></a>
  <h2>Proposed change</h2>
  <p>This wording is relative to <a href="https://eel.is/c++draft/">latest working draft</a>.
  </p>
  </div>
  <div>
    <ol>
      <ol>
        <li>
          <p>Add a new feature-test macro to 17.3.2 <a href="https://eel.is/c++draft/version.syn">[version.syn]</a>:
          </p>
          <blockquote>
            <pre><ins>#define __cpp_lib_ranges_cycle 2025XXL // <i>freestanding, also in</i> &lt;ranges&gt;</ins></pre>
          </blockquote>
        </li>

        <li>
          <p>Modify 25.2 <a href="https://eel.is/c++draft/ranges.syn">[ranges.syn]</a>, Header <tt>&lt;ranges&gt;</tt>
            synopsis, as indicated:</p>

          <blockquote>
            <pre>
#include &lt;compare&gt;              // <i>see <a href="https://eel.is/c++draft/compare.syn">[compare.syn]</a></i>
#include &lt;initializer_list&gt;     // <i>see <a href="https://eel.is/c++draft/initializer.list.syn">[initializer.list.syn]</a></i>
#include &lt;iterator&gt;             // <i>see <a href="https://eel.is/c++draft/iterator.synopsis">[iterator.synopsis]</a></i>

namespace std::ranges {
  [&hellip;]
  namespace views { inline constexpr <i>unspecified</i> enumerate = <i>unspecified</i>; }

<ins>// <i>[range.cycle], cycle view</i>
  template&lt;view&gt;
    requires forward_range&lt;V&gt;
  class cycle_view;

  namespace views { inline constexpr <i>unspecified</i> cycle = <i>unspecified</i>; }</ins>
  [&hellip;]
}
        </pre>
          </blockquote>
        </li>
        <li>
          <p>Add <b>25.7.? Cycle view [range.cycle]</b></a> after 25.7.24 <a
              href="https://eel.is/c++draft/range.enumerate">[range.enumerate]</a> as indicated:</p>
          <p>
            -1-
            A cycle view presents an infinite view that endlessly repeats the source range.
          </p>
          <p>-2- The name <code>views::cycle</code> denotes a range adaptor object (<a
              href="https://eel.is/c++draft/range.adaptor.object">[range.adaptor.object]</a>).
            Given a subexpression <code>E</code>, the expression <code>views::cycle(E)</code> is expression-equivalent
            to
            <code>cycle_view&lt;views::all_t&lt;decltype((E))&gt;&gt;{E}</code>.
          </p>
        </li>
      </ol>
      <p>-3- [<i>Example 1</i>:</p>
      <pre>
  auto cycle = views::iota(0, 3) | views::cycle;
  println("{} ", cycle | views::take(10)); // <i>prints</i> [0, 1, 2, 0, 1, 2, 0, 1, 2, 0]</pre>
      — <i>end example]</i>
      <p>[25.7.?.2] Class template <code>cycle_view</code> [range.cycle.view]</p>
      <pre>
namespace std::ranges {
  template&lt;view V&gt;
    requires forward_range&lt;V&gt;
  class cycle_view : public view_interface&lt;cycle_view&lt;V&gt;&gt; {
  private:
    V <i>base_</i> = V();                                      // <i>exposition only</i>

    // <i>[range.cycle.iterator], class</i> template cycle_view::<i>iterator</i>
    template&lt;bool Const&gt;
    class <i>iterator</i>;                                     // <i>exposition only</i>
  
  public:
    cycle_view() requires default_initializable&lt;V&gt; = default;
    constexpr explicit cycle_view(V base);

    constexpr V base() const & requires copy_constructible&lt;V&gt; { return <i>base_</i>; }
    constexpr V base() && { return std::move(<i>base_</i>); }

    constexpr auto begin() requires (!<i>simple-view</i>&lt;V&gt;)
    { return <i>iterator</i>&lt;false&gt;(*this, ranges::begin(<i>base_</i>)); }

    constexpr auto begin() const requires forward_range&lt;const V&gt; 
    { return <i>iterator</i>&lt;true&gt;(*this, ranges::begin(<i>base_</i>)); }

    constexpr auto end() const noexcept { return default_sentinel; }
  };

  template&lt;class R&gt;
    cycle_view(R&amp;&amp;) -&gt; cycle_view&lt;views::all_t&lt;R&gt;&gt;;
}</pre>
      <pre>constexpr explicit cycle_view(V base);</pre>
      <blockquote>
        <p>-1- <i>Effects</i>: Initializes <code><i>base_</i></code> with <code>std::move(base)</code>.
        </p>
      </blockquote>
      <p>[25.7.?.3] Class <code>cycle_view::<i>iterator</i></code> [range.cycle.iterator]</p>
      <pre>
namespace std::ranges {
  template&lt;view V&gt;
  class cycle_view&lt;V&gt;::<i>iterator</i> {
    using <i>Parent</i> = <i>maybe-const</i>&lt;Const, cycle_view&gt;;     // <i>exposition only</i>
    using <i>Base</i> = <i>maybe-const</i>&lt;Const, V&gt;;                // <i>exposition only</i>
    
    iterator_t&lt;<i>Base</i>&gt; <i>current_</i> = iterator_t&lt;<i>Base</i>&gt;();    // <i>exposition only</i>
    <i>Parent</i>* <i>parent_</i> = nullptr;                         // <i>exposition only</i>
    range_difference_t&lt;<i>Base</i>&gt; <i>n_</i> = 0;                   // <i>exposition only</i>

    constexpr <i>iterator</i>(<i>Parent</i>& parent, iterator_t&lt;<i>Base</i>&gt; current);   // <i>exposition only</i>

   public:
    using iterator_concept  = <i>see below</i>;
    using iterator_category = <i>see below</i>;
    using value_type        = range_value_t&lt;<i>Base</i>&gt;;
    using difference_type   = <i>see below</i>;

    <i>iterator</i>() requires default_initializable&lt;iterator_t&lt;<i>Base</i>&gt;&gt; = default;

    constexpr <i>iterator</i>(iterator&lt;!Const&gt; i)
      requires Const && convertible_to&lt;iterator_t&lt;V&gt;, iterator_t&lt;<i>Base</i>&gt;&gt;;

    constexpr iterator_t&lt;<i>Base</i>&gt; base() const;

    constexpr decltype(auto) operator*() const { return *<i>current_</i>; }

    constexpr iterator_t&lt;<i>Base</i>&gt; operator-&gt;() const 
      requires <i>has-arrow</i>&lt;iterator_t&lt;<i>Base</i>&gt;&gt;;

    constexpr <i>iterator</i>& operator++();
    constexpr <i>iterator</i> operator++(int);

    constexpr <i>iterator</i>& operator--() 
      requires <i>bidirectional-common</i>&lt;<i>Base</i>&gt; || <i>sized-random-access-range</i>&lt;<i>Base</i>&gt;;
    constexpr <i>iterator</i> operator--(int)
      requires <i>bidirectional-common</i>&lt;<i>Base</i>&gt; || <i>sized-random-access-range</i>&lt;<i>Base</i>&gt;;

    constexpr <i>iterator</i>& operator+=(difference_type n)
      requires <i>sized-random-access-range</i>&lt;<i>Base</i>&gt;;
    constexpr <i>iterator</i>& operator-=(difference_type n)
      requires <i>sized-random-access-range</i>&lt;<i>Base</i>&gt;;

    constexpr decltype(auto) operator[](difference_type n) const
      requires <i>sized-random-access-range</i>&lt;<i>Base</i>&gt;
    { return *(*this + n); }

    friend constexpr bool operator==(const <i>iterator</i>& x, const <i>iterator</i>& y);
    friend constexpr bool operator==(const <i>iterator</i>& x, default_sentinel_t);

    friend constexpr bool operator&lt;(const <i>iterator</i>& x, const <i>iterator</i>& y)
      requires random_access_range&lt;<i>Base</i>&gt;;
    friend constexpr bool operator&gt;(const <i>iterator</i>& x, const <i>iterator</i>& y)
      requires random_access_range&lt;<i>Base</i>&gt;;
    friend constexpr bool operator&lt;=(const <i>iterator</i>& x, const <i>iterator</i>& y)
      requires random_access_range&lt;<i>Base</i>&gt;;
    friend constexpr bool operator&gt;=(const <i>iterator</i>& x, const <i>iterator</i>& y)
      requires random_access_range&lt;<i>Base</i>&gt;;
    friend constexpr auto operator&lt;=&gt;(const <i>iterator</i>& x, const <i>iterator</i>& y)
        requires random_access_range&lt;<i>Base</i>&gt; && 
                 three_way_comparable&lt;iterator_t&lt;<i>Base</i>&gt;&gt;;

    friend constexpr <i>iterator</i> operator+(const <i>iterator</i>& i, difference_type n)
      requires <i>sized-random-access-range</i>&lt;<i>Base</i>&gt;;
    friend constexpr <i>iterator</i> operator+(difference_type n, const <i>iterator</i>& i)
      requires <i>sized-random-access-range</i>&lt;<i>Base</i>&gt;;
    friend constexpr <i>iterator</i> operator-(const <i>iterator</i>& i, difference_type n)
      requires <i>sized-random-access-range</i>&lt;<i>Base</i>&gt;;
    friend constexpr difference_type operator-(const <i>iterator</i>& x, const <i>iterator</i>& y)
      requires sized_sentinel_for&lt;iterator_t&lt;<i>Base</i>&gt;, iterator_t&lt;<i>Base</i>&gt;&gt; && 
               sized_range&lt;<i>Base</i>&gt;;
  
    friend constexpr range_rvalue_reference_t&lt;<i>Base</i>&gt; iter_move(const <i>iterator</i>& i)
      noexcept(noexcept(ranges::iter_move(i.<i>current_</i>)));
  };
}</pre>
      <blockquote>
        <p>-1- <code>iterator::iterator_concept</code> is defined as follows:</p>
        <ol style="list-style-type: none">
          <li>
            <p>(1.1) &mdash;
              If <code><i>Base</i></code> models <code><i>sized-random-access-range</i></code>,
              then <code>iterator_concept</code> denotes <code>random_access_iterator_tag</code>.
            </p>
          </li>
          <li>
            <p>(1.2) &mdash;
              Otherwise, if <code><i>Base</i></code> models <code><i>bidirectional-common</i></code>,
              then <code>iterator_concept</code> denotes <code>bidirectional_iterator_tag</code>.
            </p>
          </li>
          <li>
            <p>(1.3) &mdash; Otherwise, <code>iterator_concept</code> denotes <code>forward_iterator_tag</code>.</p>
          </li>
        </ol>
      </blockquote>
      <blockquote>
        <p>-2-
          <code>iterator::iterator_category</code> is defined as follows:
        </p>
        <ol style="list-style-type: none">
          <li>
            <p>(2.1) &mdash;
              Let <code>C</code> denote
              <code>iterator_traits&lt;iterator_t&lt;<i>Base</i>&gt;&gt;::iterator_category</code>.
            </p>
          </li>
          <li>
            <p>(2.2) &mdash; If <code>C</code> models
              <code>derived_from&lt;random_access_iterator_tag&gt;</code> and
              <code><i>Base</i></code> models <code>sized_range</code>,
              <code>iterator_category</code> denotes
              <code>random_access_iterator_tag</code>.
            </p>
          </li>
          <li>
            <p>(2.3) &mdash; Otherwise, if <code>C</code> models
              <code>derived_from&lt;bidirectional_iterator_tag&gt;</code> and
              <code><i>Base</i></code> models <code>common_range</code>,
              <code>iterator_category</code> denotes <code>bidirectional_iterator_tag</code>.
            </p>
          </li>
          <li>
            <p>(2.4) &mdash; Otherwise, <code>iterator_category</code> denotes <code>forward_iterator_tag</code>.</p>
          </li>
        </ol>
        <p>-3-
          <code><i>iterator</i>::difference_type</code> is an implementation-defined signed-integer-like type.
        </p>
        <p>-4- <i>Recommended practice</i>: <code><i>iterator</i>::difference_type</code> should be the smallest
          signed-integer-like
          type that is sufficiently wide to store the product of <code>range_difference_t&lt;<i>Base</i>&gt;</code> with
          itself, if such a type exists.
        </p>
      </blockquote>
      <pre>constexpr <i>iterator</i>(<i>Parent</i>& parent, iterator_t&lt;<i>Base</i>&gt; current);</pre>
      <blockquote>
        <p>-5- <i>Effects</i>: Initializes <code><i>current_</i></code> with <code>std::move(current_)</code> and
          <code><i>parent_</i></code> with
          <code>addressof(parent)</code>.
        </p>
      </blockquote>
      <pre>constexpr <i>iterator</i>(iterator&lt;!Const&gt; i)
      requires Const && convertible_to&lt;iterator_t&lt;V&gt;, iterator_t&lt;<i>Base</i>&gt;&gt;;</pre>
      <blockquote>
        <p>-6- <i>Effects</i>: Initializes <code><i>current_</i></code> with <code>std::move(i.<i>current_</i>)</code>,
          <code><i>parent_</i></code> with
          <code>i.<i>parent_</i></code>, and <code><i>n_</i></code> with <code>i.<i>n_</i></code>.
        </p>
      </blockquote>
      <pre>constexpr iterator_t&lt;<i>Base</i>&gt; base() const;</pre>
      <blockquote>
        <p>-7- <i>Effects</i>: Equivalent to: <code>return <i>current_</i>;</code>
        </p>
      </blockquote>
      <pre>constexpr iterator_t&lt;<i>Base</i>&gt; operator-&gt;() const 
      requires <i>has-arrow</i>&lt;iterator_t&lt;<i>Base</i>&gt;&gt;;</pre>
      <blockquote>
        <p>-8- <i>Effects</i>: Equivalent to: <code>return <i>current_</i>;</code>
        </p>
      </blockquote>
      <pre>constexpr <i>iterator</i>& operator++();</pre>
      <blockquote>
        <p>-9- <i>Effects</i>: Equivalent to:
        </p>
        <pre>
  if (++<i>current_</i> == ranges::end(<i>parent_</i>-&gt;<i>base_</i>)) {
    <i>current_</i> = ranges::begin(<i>parent_</i>-&gt;<i>base_</i>);
    ++<i>n_</i>;
  }
  return *this;</pre>
      </blockquote>
      <pre>constexpr <i>iterator</i> operator++(int);</pre>
      <blockquote>
        <p>-10- <i>Effects</i>: Equivalent to:
        </p>
        <pre>
  auto tmp = *this;
  ++*this;
  return tmp;</pre>
      </blockquote>
      <pre>constexpr <i>iterator</i>& operator--()
  requires <i>bidirectional-common</i>&lt;<i>Base</i>&gt; || <i>sized-random-access-range</i>&lt;<i>Base</i>&gt;;</pre>
      <blockquote>
        <p>-11- <i>Effects</i>: Equivalent to:
        </p>
        <pre>
  if (<i>current_</i> == ranges::begin(<i>parent_</i>-&gt;<i>base_</i>)) {
    if constexpr (common_range&lt;<i>Base</i>&gt;)
      <i>current_</i> = ranges::end(<i>parent_</i>-&gt;<i>base_</i>);
    else
      <i>current_</i> = ranges::begin(<i>parent_</i>-&gt;<i>base_</i>) + ranges::distance(<i>parent_</i>-&gt;<i>base_</i>);
    --<i>n_</i>;
  }
  --<i>current_</i>;
  return *this;</pre>
      </blockquote>
      <pre>constexpr <i>iterator</i>& operator--(int)
  requires requires <i>bidirectional-common</i>&lt;<i>Base</i>&gt; || <i>sized-random-access-range</i>&lt;<i>Base</i>&gt;;</pre>
      <blockquote>
        <p>-12- <i>Effects</i>: Equivalent to:
        </p>
        <pre>  auto tmp = *this;
  --*this;
  return tmp;
  </pre>
      </blockquote>
      <pre>constexpr <i>iterator</i>& operator+=(difference_type n)
  requires <i>sized-random-access-range</i>&lt;<i>Base</i>&gt;;</pre>
      <blockquote>
        <p>-13- <i>Effects</i>: Equivalent to:</p>
        <pre>
  const auto first = ranges::begin(<i>parent_</i>-&gt;<i>base_</i>);
  const auto dist = ranges::distance(<i>parent_</i>-&gt;<i>base_</i>);
  const auto offset = <i>current_</i> - first;
  const auto new_n = n + offset;
  const auto new_offset = new_n % dist;
  <i>n_</i> += new_n / dist;
  <i>current_</i> = first + range_difference_t&lt;<i>Base</i>&gt;(new_offset &gt;= 0 ? new_offset : new_offset + dist);
  return *this;</pre>
      </blockquote>
      <pre>constexpr <i>iterator</i>& operator-=(difference_type x)
  requires <i>sized-random-access-range</i>&lt;<i>Base</i>&gt;;</pre>
      <blockquote>
        <p>-14- <i>Effects</i>: Equivalent to: <code>return *this += -x;</code>
        </p>
      </blockquote>
      <pre>friend constexpr bool operator==(const <i>iterator</i>& x, const <i>iterator</i>& y);</pre>
      <blockquote>
        <p>-15- <i>Returns</i>: <code>x.<i>n_</i> == y.<i>n_</i> && x.<i>current_</i> == x.<i>current_</i></code>.
        </p>
      </blockquote>
      <pre>friend constexpr bool operator==(const <i>iterator</i>& x, default_sentinel_t);</pre>
      <blockquote>
        <p>-16- <i>Returns</i>: <code>ranges::empty(x.<i>parent_</i>-&gt;<i>base_</i>)</code>.
        </p>
      </blockquote>
      <pre>friend constexpr bool operator&lt;(const <i>iterator</i>& x, const <i>iterator</i>& y)
  requires random_access_range&lt;<i>Base</i>&gt;;
friend constexpr bool operator&gt;(const <i>iterator</i>& x, const <i>iterator</i>& y)
  requires random_access_range&lt;<i>Base</i>&gt;;
friend constexpr bool operator&lt;=(const <i>iterator</i>& x, const <i>iterator</i>& y)
  requires random_access_range&lt;<i>Base</i>&gt;;
friend constexpr bool operator&gt;=(const <i>iterator</i>& x, const <i>iterator</i>& y)
  requires random_access_range&lt;<i>Base</i>&gt;;</pre>
      <blockquote>
        <p>-17- Let <code><i>op</i></code> be the operator.</p>
        <p>-18- <i>Effects</i>: Equivalent to:
        <pre>
  if (x.<i>n_</i> != y.<i>n_</i>)
    return x.<i>n_</i> <i>op</i> y.<i>n_</i>;
  return x.<i>current_</i> <i>op</i> y.<i>current_</i>;
</pre>
        </p>
      </blockquote>
      <pre>
friend constexpr auto operator&lt;=&gt;(const <i>iterator</i>& x, const <i>iterator</i>& y)
  requires random_access_range&lt;<i>Base</i>&gt; && 
           three_way_comparable&lt;iterator_t&lt;<i>Base</i>&gt;&gt;;
</pre>
      <blockquote>
        <p>-19- <i>Effects</i>: Equivalent to:</p>
        <pre>
  using R = compare_three_way_result_t&lt;iterator_t&lt;<i>Base</i>&gt;&gt;;
  if (x.<i>n_</i> != y.<i>n_</i>)
    return R(x.<i>n_</i> &lt;=&gt; y.<i>n_</i>);
  return R(x.<i>current_</i> &lt;=&gt; y.<i>current_</i>);
</pre>
      </blockquote>
      <pre>friend constexpr <i>iterator</i> operator+(const <i>iterator</i>& i, difference_type n)
  requires <i>sized-random-access-range</i>&lt;<i>Base</i>&gt;;
friend constexpr <i>iterator</i> operator+(difference_type n, const <i>iterator</i>& i)
  requires <i>sized-random-access-range</i>&lt;<i>Base</i>&gt;;</pre>
      <blockquote>
        <p>-20- <i>Effects</i>: Equivalent to:
        <pre>
  auto r = i;
  r += n;
  return r;
</pre>
        </p>
      </blockquote>
      <pre>friend constexpr <i>iterator</i> operator-(const <i>iterator</i>& i, difference_type n)
  requires <i>sized-random-access-range</i>&lt;<i>Base</i>&gt;;</pre>
      <blockquote>
        <p>-21- <i>Effects</i>: Equivalent to:
        <pre>
  auto r = i;
  r -= n;
  return r;
</pre>
        </p>
      </blockquote>
      <pre>friend constexpr difference_type operator-(const <i>iterator</i>& x, const <i>iterator</i>& y)
  requires sized_sentinel_for&lt;iterator_t&lt;<i>Base</i>&gt;, iterator_t&lt;<i>Base</i>&gt;&gt; && 
           sized_range&lt;<i>Base</i>&gt;;</pre>
      <blockquote>
        <p>-22- <i>Effects</i>: Equivalent to:
        <pre>
  const auto dist = ranges::distance(x.<i>parent_</i>-&gt;<i>base_</i>);
  return (x.<i>n_</i> - y.<i>n_</i>) * dist + x.<i>current_</i> - y.<i>current_</i>;</pre>
        </p>
      </blockquote>
      <pre>friend constexpr range_rvalue_reference_t&lt;<i>Base</i>&gt; iter_move(const <i>iterator</i>& i)
  noexcept(noexcept(ranges::iter_move(i.<i>current_</i>)));</pre>
      <blockquote>
        <p>-23- <i>Effects</i>: Equivalent to: <code>return ranges::iter_move(i.<i>current_</i>);</code>
        </p>
      </blockquote>
      </li>
    </ol>
    </ol>
  </div>
  <a name="References"></a>
  <h2>References</h2>
  <dd>
  <dt id="biblio-p2760">[P2760R1]
  <dd>Barry Revzin. A Plan for C++26 Ranges. URL: <a
      href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2760r1.html">https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2760r1.html</a>
</body>

</html>