<html>

<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title><code>views::slice</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>P3216R1</td>
      </tr>
      <tr>
        <td>Date</td>
        <td>2025-07-01</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::slice</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 the Tier 1 adaptor <code>views::slice</code> (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.
    Notably, this is the first standard range adaptor that accepts two arguments — <code>start</code> and
    <code>end</code> — to specify the interval
    [<code>start</code>, <code>end</code>) for slicing a range.
  </p>
  <a name="Revision history"></a>
  <h2>Revision history</h2>
  <p>
  <h3>R0</h3>
  <p>Initial revision.</p>
  <h3>R1</h3>
  <p>Introduce new <code>slice_view</code> class based on feedback from SG9 St. Louis.</p>
  </p>
  </ol>
  </p>
  <a name="Discussion"></a>
  <h2>Discussion</h2>
  <p>
    Slicing — a means of extracting a contiguous subrange from a sequence by specifying a start and end index — is a
    fundamental operation in modern programming.
    Many mainstream languages, such as Python and Rust, offer built-in slice syntax, making it a familiar and expected
    feature for developers. In the C++ ecosystem, while the Ranges library has greatly enhanced composability and
    expressiveness, it currently lacks a direct, ergonomic, and standard way to perform slicing by index.
  </p>
  <h3>Why <code>views::slice</code> is needed</h3>
  <ul>
    <li>Familiarity</li>
    <p>Developers often need to access a specific segment of a range, whether for data processing, algorithmic
      partitioning, or windowed computations. Introducing
      <code>views::slice</code> aligns C++ with the expectations set by other languages, reducing the cognitive gap for
      new and
      experienced programmers alike.
    </p>
    <li>Readability</li>
    <p>While it is possible to achieve slicing today with <code>views::drop(start) |
      views::take(end - start)</code>, this composition, while valid, may obscure the programmer's intent due to its
      verbosity. A single <code>views::slice(start,
      end)</code> call is both concise and self-documenting,
      improving code clarity and maintainability.</p>
    <li>Generic and Robust Semantics</li>
    <p>Unlike <code>subrange</code> and <code>counted</code>, which have limitations when working with only-input or
      non-sized range types,
      <code>views::slice</code> can be designed to work generically with any range, making it broadly applicable. Its
      semantics — returning the interval [<code>start</code>, <code>end</code>) and gracefully handling
      out-of-bounds indices — are both predictable and robust.
    </p>
  </ul>
  <p>Given the above,
    the introduction of <code>views::slice</code> fills a clear gap in the C++ Ranges library, providing a direct,
    expressive, and
    interoperable way to extract subranges by index. It aligns C++ with industry standards, improves code readability,
    and empowers developers to write more concise and correct range-based code. For these reasons,
    <code>views::slice</code> is a
    valuable and timely addition to C++29:
  </p>

  <pre>
  string_view text = "Hello, world!";
  auto sub1 = text | views::slice(7, 12);  // <i>"world"</i>

  vector v = {1, 2, 3, 4, 5};
  auto sub3 = v | views::slice(1, 10);     // <i>[2, 3, 4, 5]</i>
  auto sub2 = v | views::slice(2, 2);      // <i>empty range</i>
  auto sub4 = v | views::slice(5, 10);     // <i>empty range</i></pre>
  </ol>
  <a name="Design"></a>
  <h2>Design</h2>
  <h3>The second argument should be <i>end</i> instead of <i>size</i></h3>
  <p>An alternative design for <code>slice</code> is to accept a starting index and a <i>size</i>. However, the author
    prefers to use the end index as the second parameter as this is intuitive and consistent with
    other language syntaxes:</p>


  <table border="1" cellpadding="6" cellspacing="0">
    <thead>
      <tr>
        <th>Language</th>
        <th>Syntax</th>
        <th>Stride Support</th>
        <th>Negative Indices</th>
        <th>Out-of-Bounds Behavior</th>
        <th>Notes</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Python</td>
        <td><code>a[start:end:step]</code></td>
        <td>✅ Yes</td>
        <td>✅ Yes</td>
        <td>✅ Truncated</td>
        <td></td>
      </tr>
      <tr>
        <td>Rust</td>
        <td><code>&a[start..end]</code></td>
        <td>❌ No</td>
        <td>❌ No</td>
        <td>❌ Panics</td>
        <td>Need to use <code>.iter().<span style="color:red;font-weight:bolder">step_by(n)</span></code> to stride</td>
      </tr>
      <tr>
        <td>Go</td>
        <td><code>a[start:end]</code></td>
        <td>❌ No</td>
        <td>❌ No</td>
        <td>❌ Panics</td>
        <td><code>a[start:end:<span style="color:red;font-weight:bolder">max</span>]</code> controls capacity, not
          stride</td>
      </tr>
      <tr>
        <td>JavaScript</td>
        <td><code>a.slice(start, end)</code></td>
        <td>❌ No</td>
        <td>✅ Yes</td>
        <td>✅ Truncated</td>
        <td></td>
      </tr>
      <tr>
        <td>Ruby</td>
        <td><code>a[start, length]</code> or <code>a[start..end]</code></td>
        <td>❌ No</td>
        <td>✅ Yes</td>
        <td>✅ Truncated</td>
        <td></td>
      </tr>
    </tbody>
  </table>
  <p> This is the de facto standard for slicing.</p>
  <h3>Motivation for a dedicated <code>slice_view</code> class</h3>
  <p>
    As stated in <a
      href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2214r2.html#the-takedrop-family">P2214</a>:
    "<code>slice(M, N)</code>
    is equivalent to <code>views::drop(M) | views::take(N - M)</code>, and you couldn't do much better as a first class
    view.
    range-v3 also supports a flavor that works as <code>views::slice(M, end - N)</code> for a special variable
    <code>end</code>, which likewise be
    equivalent to <code>r | views::drop(M) | views::drop_last(N)</code>."
  <p>
    While <code>views::drop</code> and <code>views::take</code> provide compositional power, a dedicated
    <code>slice_view</code> offers better API consistency, performance, and expressiveness. It enables more robust and
    efficient handling of subranges for the following reasons:
  </p>
  </p>
  <ul>
    <li>
      <code>base()</code> member Consistency
    </li>
    <p>When composing <code>views::drop(M) | views::take(N - M)</code>, the
      resulting view is a <code>take_view</code>. Calling <code>base()</code> on this returns the underlying
      <code>drop_view</code> rather than the original range. This breaks the expectation that <code>base()</code> should
      always provide access to the original range,
      making code that needs to retrieve the true base more indirect as <code>.base().base()</code>.
      <li>
        Global Range Awareness and <code>reserve_hint()</code></li>
    <p>A dedicated <code>slice_view</code> holds both the start
      and end indices, allowing it to globally understand the intended subrange. This enables it to unconditionally
      provide a <code>reserve_hint()</code> member that returns <code>end - start</code>, accurately reflecting the
      expected size of the slice in most cases. With a drop-take composition, this global knowledge is lost, making it
      difficult or impossible to provide such optimizations. For example, <code>drop_view</code> only
      provides <code>reserve_hint()</code> if the underlying range models <code>approximately_sized_range</code>.</p>
    </li>
    <li>Performance Overhead</li>
    <p>Indirect composition through multiple views (i.e., layering
      <code>drop_view</code> and <code>take_view</code>) can introduce additional iterator wrappers and indirections.
      This may result in reduced performance due to extra function calls and less efficient inlining or optimization
      opportunities compared to a single, purpose-built <code>slice_view</code>.
    </p>
    <li>Unified Boundary Handling</li>
    <p>A dedicated <code>slice_view</code> can perform comprehensive boundary
      checks and handle out-of-range conditions in a single place, ensuring predictable and robust behavior. In
      contrast, drop-take composition splits responsibility, potentially leading to inconsistent or redundant checks.
    </p>
    <li>Better Debug-ability</li>
    <p> A dedicated <code>slice_view</code> results in a named, standalone view type that directly reflects the user's
      intent. In contrast, composing <code>drop_view</code> and <code>take_view</code> layers results in a nested and
      opaque type, which can be harder to inspect or reason about in debugging sessions.</p>
    <li>Better Future Extensibility</li>
    <p><code>slice_view</code> makes it easier to add new features or enhancements in the future. Any extensions or
      improvements can be implemented directly in <code>slice_view</code> without needing to consider the internal logic
      or compatibility of both <code>drop_view</code> and <code>take_view</code>. This simplifies maintenance and
      enables more flexible evolution of the slicing functionality.</p>
  </ul>
  <h3>Handling of out-of-bounds</h3>
  <p>
    It should be noted that in range/v3, <code>views::slice</code> is also implemented with a dedicated view class, but
    it does
    <strong>not</strong> perform any boundary checking. It always assumes that the provided <code>start</code> and
    <code>end</code> indices are within valid bounds of the underlying range:
  <pre>
  auto ints  = {1, 2, 3, 4, 5};
  auto slice = ints | ranges::v3::views::slice(3, 9);
  std::println("{}", slice); // prints [4, 5, <span style="color:red;font-weight:bolder">0, 0, 0, 2147483647</span>]</pre>
  </p>
  <p>
    This design makes its <code>views::slice</code> effectively an unchecked version of slicing. If the indices are out
    of range, the behavior is undefined, potentially leading to runtime errors or undefined behavior.
    From a naming perspective, the <code>range/v3</code> version would be more accurately described as
    <code>views::unchecked_slice</code> or <code>views::slice_exactly</code>, reflecting its lack of safety checks.
  </p>
  <p>
    In contrast, the proposed <code>views::slice</code> includes comprehensive boundary checking just like
    <code>views::take</code> and <code>views::drop</code>; it will
    safely adjust or clamp the specified indices as appropriate, ensuring well-defined and predictable behavior.
  </p>
  </p>
  <h3>Special variable <code><i>end</i></code> is not support</h3>
  <p>
    In range-v3, the special variable <code><i>end</i></code> is supported in <code>views::slice</code>,
    allowing users to write expressions like <code>views::slice(M, <i>end</i> - N)</code> to indicate slicing from
    index <code>M</code> up to <code>N</code> elements before the end of the range. </p>
  <p>While this can be expressive in certain scenarios, the author believes it is unnecessary and potentially
    problematic
    for several reasons.</p>

  First, introducing a special variable such as <code><i>end</i></code> can make the syntax less clear and more
  confusing, especially for users who expect a straightforward two-index slicing interface similar to what is found in
  other mainstream languages.
  This added complexity may hinder readability and increase the learning curve for new users.</p>
  <p>
    Second, and more importantly, range-v3's implementation does not perform boundary checking for the end. It assumes
    that the
    indices provided are always valid,
    which is fundamentally different from our proposed design.
    Supporting end-based expressions in a boundary-checked implementation introduces challenges,
    particularly for input ranges, since evaluating something like <code><i>end</i> - N</code> would require traversing
    the entire range,
    which is infeasible for single-pass input ranges.</p>
  <p>In summary, while the <code><i>end</i></code> variable enables some expressive patterns, it complicates the
    interface and is incompatible with a robust, boundary-checked design.
    For these reasons, the author does not adopting this feature in the proposal.</p>
  <h3>Stride overload is not provided</h3>
  <p>
    As described in the table above, Python also allows an optional stride (step) parameter,
    its slice syntax <code>[start:end:step]</code> enables users to select every nth element or even
    reverse the sequence by specifying a negative stride.</p>
  <p>However, JavaScript, Go, and many other languages with slicing capabilities (such as Ruby, Swift, or Kotlin) do not
    include stride as part of their native slice syntax;
    instead, users must use separate functions or methods to achieve similar effects. Rust's standard slice syntax does
    not support stride directly; users must use iterators like <code>.iter().step_by(n)</code> to achieve striding.</p>
  <p>
    This supports the case against overloading <code>views::slice</code> with a stride parameter
    in C++, which, already provides a clear and composable way to achieve striding via <code>views::stride</code>.
    Chaining adaptors like <code>views::slice(M, N) | views::stride(P)</code> makes the intent and order of operations
    clear, whereas
    adding a stride overload to <code>views::slice</code> could blur the distinction between slicing and stepping,
    making the API
    less intuitive.
  </p>
  <p>For these reasons, the author does not support stride.</p>
  <h3>Specialization for return types</h3>
  <p>
    For certain well-known range types such as <code>iota_view</code>, <code>subrange</code>, or <code>span</code>, and
    so on,
    <code>views::slice(start, end)</code> can be optimized to return a view of the same type with adjusted bounds,
    rather than wrapping it in a generic <code>slice_view</code>.
    This follows the precedent set by <code>views::take</code> and <code>views::drop</code>, and can lead to fewer
    template instantiations, improved compile times, and better performance.
  </p>
  <h3>Handling of non-sized range</h3>
  <p>For ranges that model <code>sized_range</code>, the subrange's start and end positions can be computed precisely.
    In such cases, the implementation can advance the iterator by the specified offset and construct a
    <code>counted_iterator</code> with the exact number of elements in the slice.
  </p>
  <p>
    For ranges that do not model <code>sized_range</code>, the precise end position <i>cannot</i> be determined in
    advance.
    The implementation must instead use the bound-preserving overload of <code>ranges::next</code> to advance the
    iterator to the starting position.
    The resulting iterator is then wrapped in a <code>counted_iterator</code> with the <i>desired</i> count.
    Since the base range may not contain enough elements, a custom sentinel is used to detect the actual end of the
    range.
    This sentinel terminates iteration either when the count is exhausted or when the underlying iterator reaches the
    end of the base range, similar to the behavior of <code>take_view::<i>sentinel</i></code>.</p>
  <h3>Conditionally <code>const</code>-iterable</h3>
  <p>
    To satisfy the requirement that <code>begin()</code> of a view operates in amortized constant time,
    the implementation must cache the computed starting iterator when it cannot be obtained in constant time.
    In particular, when the base range does not model <code>random_access_range</code> or <code>sized_range</code>,
    advancing the <code>begin()</code> iterator to the desired offset may require O(n) time.
    In such cases, the result of this computation must be stored internally to avoid repeated traversal on subsequent
    calls to <code>begin()</code>,
    which means that <code>slice_view</code> may not be <code>const</code>-iterable in all cases.
  </p>
  <a name="Implementation experience"></a>
  <h2>Implementation experience</h2>
  <p>The author implemented <code>views::slice</code> based on libstdc++, see <a
      href="https://godbolt.org/z/o3aYd7jzT">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_slice 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> drop_while = <i>unspecified</i>; }

<ins>// <i>[range.slice], slice view</i>
  template&lt;view&gt; class slice_view;

  template&lt;class T&gt;
    constexpr bool enable_borrowed_range&lt;slice_view&lt;T&gt;&gt; =
      enable_borrowed_range&lt;T&gt;;

  namespace views { inline constexpr <i>unspecified</i> slice = <i>unspecified</i>; }</ins>
  [&hellip;]
}
        </pre>
          </blockquote>
        </li>
        <li>
          <p>Add <b>25.7.? Slice view [range.slice]</b></a> after 25.7.13 <a
              href="https://eel.is/c++draft/range.drop.while">[range.drop.while]</a> as indicated:</p>
          <p>
            -1-
            A slice view presents elements from index <i>N</i> (inclusive) up to index <i>M</i> (exclusive) of another
            view,
            or all elements from <i>N</i> to the end if <i>M</i> exceeds the range, or an empty view if there are fewer
            than <i>N</i> elements.
          </p>
          <p>-2- The name <code>views::slice</code> denotes a range adaptor object (<a
              href="https://eel.is/c++draft/range.adaptor.object">[range.adaptor.object]</a>).
            Let <code>E</code>, <code>F</code> and <code>G</code> be expressions, let <code>T</code> be
            <code>remove_cvref_t&lt;decltype((E))&gt;</code> and <code>D</code> be
            <code>range_difference_t&lt;decltype((E))&gt;</code>. If
            <code>decltype((F))</code> and <code>decltype((G))</code> do not model
            <code>convertible_to&lt;D&gt;</code>, <code>views::slice(E, F, G)</code> is ill-formed. Otherwise, the
            expression <code>views::slice(E, F, G)</code> is expression-equivalent to:
          </p>
          <ol style="list-style-type: none">
            <li>
              <p>(2.1) &mdash; If T is a specialization of <code>empty_view</code>, <code>repeat_view</code>,
                <code>span</code>, or <code>basic_string_view</code>, or
                <code>T</code> models <code>random_access_range</code> and <code>sized_range</code> and
                is a specialization of <code>iota_view</code>, or
                <code>subrange</code>,
                then <code>E | views::drop(F) | views::take(static_cast&lt;D&gt;(G) - static_cast&lt;D&gt;(F))</code>,
                except that <code>F</code> is evaluated only once.
            </li>
            <li>
              <p>(2.2) &mdash; Otherwise, <code>slice_view(E, F, G)</code>.
              </p>
            </li>
          </ol>
          <p>-3- [<i>Example 1</i>:</p>
          <pre>
  auto ints = views::iota(0);
  auto fifties = ints | views::slice(50, 60);
  println("{} ", fifties); // <i>prints</i> [50, 51, 52, 53, 54, 55, 56, 57, 58, 59]</pre>
          — <i>end example]</i>
          <p>[25.7.?.2] Class template <code>slice_view</code> [range.slice.view]</p>
          <pre>
namespace std::ranges {
  template&lt;view V&gt;
  class slice_view : public view_interface&lt;slice_view&lt;V&gt;&gt; {
  private:
    V <i>base_</i> = V();                                      // <i>exposition only</i>
    range_difference_t&lt;V&gt; <i>from_</i> = 0;                    // <i>exposition only</i>
    range_difference_t&lt;V&gt; <i>to_</i> = 0;                      // <i>exposition only</i>

    // <i>[range.slice.sentinel], class slice_view::sentinel</i>
    class <i>sentinel</i>;                                     // <i>exposition only</i>
  
  public:
    slice_view() requires default_initializable&lt;V&gt; = default;
    constexpr explicit slice_view(V base, range_difference_t&lt;V&gt; from, 
                                          range_difference_t&lt;V&gt; to);

    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; &amp;&amp; 
                  random_access_range&lt;const V&gt; &amp;&amp; sized_range&lt;const V&gt;));

    constexpr auto begin() const
      requires random_access_range&lt;const V&gt; &amp;&amp; sized_range&lt;const V&gt; {
      return ranges::begin(<i>base_</i>) + std::min(<i>from_</i>, ranges::distance(<i>base_</i>));
    }

    constexpr auto end() 
      requires (!(<i>simple-view</i>&lt;V&gt; &amp;&amp; 
                  random_access_range&lt;const V&gt; &amp;&amp; sized_range&lt;const V&gt;)) { 
      if constexpr (sized_range&lt;V&gt;) {
        if constexpr (random_access_range&lt;V&gt;)
          return ranges::begin(<i>base_</i>) + std::min(<i>to_</i>, ranges::distance(<i>base_</i>));
        else
          return default_sentinel;
      } else if constexpr (sized_sentinel_for&lt;sentinel_t&lt;V&gt;, iterator_t&lt;V&gt;&gt;)
        return default_sentinel;
      else
        return <i>sentinel</i>(ranges::end(<i>base_</i>));
    }

    constexpr auto end() const
      requires random_access_range&lt;const V&gt; &amp;&amp; sized_range&lt;const V&gt; { 
      return ranges::begin(<i>base_</i>) + std::min(<i>to_</i>, ranges::distance(<i>base_</i>));
    }

    constexpr auto size() requires sized_range&lt;V&gt; {
      auto n = ranges::distance(<i>base_</i>);
      return static_cast&lt;range_size_t&lt;V&gt;&gt;(std::min(<i>to_</i>, n) - std::min(<i>from_</i>, n));
    }

    constexpr auto size() const requires sized_range&lt;const V&gt; {
      auto n = ranges::distance(<i>base_</i>);
      return static_cast&lt;range_size_t&lt;const V&gt;&gt;(std::min(<i>to_</i>, n) - std::min(<i>from_</i>, n));
    }

    constexpr auto reserve_hint() {
      if constexpr (approximately_sized_range&lt;V&gt;) {
        auto n = static_cast&lt;range_difference_t&lt;V&gt;&gt;(ranges::reserve_hint(<i>base_</i>));
        return <i>to-unsigned-like</i>(std::min(<i>to_</i>, n) - std::min(<i>from_</i>, n));
      }
      return <i>to-unsigned-like</i>(<i>to_</i> - <i>from_</i>);
    }

    constexpr auto reserve_hint() const {
      if constexpr (approximately_sized_range&lt;const V&gt;) {
        auto n = static_cast&lt;range_difference_t&lt;const V&gt;&gt;(ranges::reserve_hint(<i>base_</i>));
        return <i>to-unsigned-like</i>(std::min(<i>to_</i>, n) - std::min(<i>from_</i>, n));
      }
      return <i>to-unsigned-like</i>(<i>to_</i> - <i>from_</i>);
    }
  };

  template&lt;class R&gt;
    slice_view(R&amp;&amp;, range_difference_t&lt;R&gt;, range_difference_t&lt;R&gt;)
      -&gt; slice_view&lt;views::all_t&lt;R&gt;&gt;;
}</pre>
          <pre>constexpr explicit slice_view(V base, range_difference_t&lt;V&gt; from, 
                                      range_difference_t&lt;V&gt; to);</pre>
          <blockquote>
            <p>-1- <i>Preconditions</i>: <code>from &gt;= 0</code> and <code>from &lt;= to</code> are each
              <code>true</code>.
            </p>
            <p>-2- <i>Effects</i>: Initializes <code><i>base_</i></code> with <code>std::move(base)</code>,
              <code><i>from_</i></code> with <code>from</code>, and <code><i>to_</i></code> with <code>to</code>.
            </p>
          </blockquote>
          <pre>constexpr auto begin()
  requires (!(<i>simple-view</i>&lt;V&gt; &amp;&amp; 
              random_access_range&lt;const V&gt; &amp;&amp; sized_range&lt;const V&gt;));</pre>
          <blockquote>
            <p>
              -3- <i>Returns</i>:
            </p>
            <ol style="list-style-type: none">
              <li>
                <p>(3.1) &mdash; If <code>V</code> models <code>sized_range</code>:
                </p>
                <ol style="list-style-type: none">
                  <li>
                    <p>(3.1.1) &mdash; If <code>V</code> models <code>random_access_range</code>,</p>
                    <pre>  ranges::begin(<i>base_</i>) + std::min(<i>from_</i>, ranges::distance(<i>base_</i>))</pre>
                  </li>
                  <li>
                    <p>(3.1.2) &mdash; Otherwise, let <code>n</code> be <code>ranges::distance(<i>base_</i>)</code>,
                    </p>
                    <pre>  counted_iterator(ranges::next(ranges::begin(<i>base_</i>), std::min(<i>from_</i>, n)),
                   std::min(<i>to_</i>, n) - std::min(<i>from_</i>, n))
                  </pre>
                  </li>
                </ol>
              <li>
                <p>(3.2) &mdash; Otherwise, if <code>sentinel_t&lt;V&gt;</code> models
                  <code>sized_sentinel_for&lt;iterator_t&lt;V&gt;&gt;</code>,
                  let <code>it</code> be <code>ranges::begin(<i>base_</i>)</code> and <code>n</code> be
                  <code>ranges::end(<i>base_</i>) - it</code>,
                </p>
                <pre>  counted_iterator(ranges::next(std::move(it), std::min(<i>from_</i>, n)), 
                   std::min(<i>to_</i>, n) - std::min(<i>from_</i>, n))</pre>
              </li>
              <li>
                <p>(3.3) &mdash; Otherwise, <code>counted_iterator(ranges::next(ranges::begin(<i>base_</i>), <i>from_</i>, ranges::end(<i>base_</i>)),
                              <i>to_</i> - <i>from_</i>)</code>.</p>
              </li>
            </ol>
            <p>
              -4- <i>Remarks</i>:
              In order to provide the amortized constant-time complexity required by the <code>range</code> concept
              when <code>slice_view</code> models <code>forward_range</code>, this function caches the
              result within the
              <code>slice_view</code> for use on subsequent calls.
            </p>
            [<i>Note 1</i>: Without this, applying a <code>reverse_view</code> over a <code>slice_view</code>
            would have
            quadratic iteration complexity. &mdash; <i>end note</i>]
            </p>
          </blockquote>
          <p>[25.7.?.3] Class <code>slice_view::<i>sentinel</i></code> [range.slice.sentinel]</p>
          <pre>
namespace std::ranges {
  template&lt;view V&gt;
  class slice_view&lt;V&gt;::<i>sentinel</i> {
  private:
    sentinel_t&lt;V&gt; <i>end_</i> = sentinel_t&lt;V&gt;();    // <i>exposition only</i>

  public:
    <i>sentinel</i>() = default;
    constexpr explicit <i>sentinel</i>(sentinel_t&lt;V&gt; end);

    constexpr sentinel_t&lt;V&gt; base() const;

    friend constexpr bool operator==(const counted_iterator&lt;iterator_t&lt;V&gt;&gt;&amp; x, const sentinel&amp; y);
  };
}</pre>
          <pre>constexpr explicit <i>sentinel</i>(sentinel_t&lt;V&gt; end);</pre>
          <blockquote>
            <p>-1- <i>Effects</i>: Initializes <code><i>end_</i></code> with <code>end</code>.
            </p>
          </blockquote>
          <pre>constexpr sentinel_t&lt;V&gt; base() const;</pre>
          <blockquote>
            <p>-2- <i>Effects</i>: Equivalent to: <code>return <i>end_</i>;</code>
            </p>
          </blockquote>
          <pre>friend constexpr bool operator==(const counted_iterator&lt;iterator_t&lt;V&gt;&gt;&amp; x, const <i>sentinel</i>&amp; y);</pre>
          <blockquote>
            <p>-3- <i>Effects</i>: Equivalent to: <code>return x.count() == 0 || x.base() == y.<i>end_</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>