<html>

<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title><code>views::unchecked_(take|drop)</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>P3230R1</td>
      </tr>
      <tr>
        <td>Date</td>
        <td>2024-12-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::unchecked_(take|drop)</code></h1>
  <ul>
    <li>
      <ul>
        <li>Abstract</li>
        <li>Revision history</li>
        <li>Discussion</li>
        <li>Design</li>
        <li>Implementation experience</li>
        <li>Benchmarks</li>
        <li>Proposed change</li>
        <li>References</li>
      </ul>
    </li>
  </ul>
  <a name="Abstract"></a>
  <h2>Abstract</h2>
  <p>
    This paper proposes two Tier 1 adaptors in <a
      href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2760r1.html">P2760</a>:
    <code>views::unchecked_drop</code> and <code>views::unchecked_take</code>, cousins of <code>drop</code>
    and <code>take</code>, to improve the C++26
    ranges facilities, which were renamed to <code>views::unchecked_take</code> and
    <code>views::unchecked_drop</code> after Tokyo WG21,
    as "unchecked" better describes the utility's nature than "exactly".
  </p>
  <a name="Revision history"></a>
  <h2>Revision history</h2>
  <p>
  <h3>R1</h3>
  <p>
    <li> Added <i>unchecked</i> part for the naming.</li>
  </p>
  <p>
    <li> Added benchmarks.</li>
  </p>
  <p>
    <li> Remove <tt>empty_view</tt> specialization for return types.</li>
  </p>
  <h3>R0</h3>
  <p>Initial revision.</p>
  </p>
  </ol>
  </p>
  <a name="Discussion"></a>
  <h2>Discussion</h2>
  <p>
    <code>unchecked_take</code> and <code>unchecked_drop</code> are very similar to <code>take</code> and
    <code>drop</code>.
    The only difference is that the former requires the selection size to be no larger than the size of the original
    range; otherwise, it is <i>Undefined Behavior</i>.
  <p>
    Since there is no boundary check, <code>views::unchecked_<i>meow</i></code> is more efficient than
    <code>views::<i>meow</i></code> for situations where the user already knows that there are enough elements
    in the range, which is what "unchecked" is all about.
  </p>
  </ol>
  <a name="Design"></a>
  <h2>Design</h2>
  <p>
  <h3>
    Should we name it <code>views::unchecked_<i>meow</i></code> or <code>views::<i>meow</i>_unchecked</code>?
  </h3>
  <p>
    To emphasize the "unchecked" part, it is natural to think about whether to put it before or after "take".</p>
  </p>The author prefers to <code>unchecked_<i>meow</i></code> to align with the naming convention in the
  current standard, given that
  we already have <code>inplace_vector::unchecked_push_back</code>.
  If we put it at the end, it should be more accurate to name it <code><i>meow</i>_uncheckly</code>, however,
  "uncheckly" is not quite a common word.
  </p>
  <h3>
    Should we introduce <code>unchecked_<i>meow</i>_view</code> classes?
  </h3>
  <p>
    Although both can achieve similar effects with existing utilities, e.g.
    <code>unchecked_take(r, N)</code> is somewhat equivalent to <code>views::counted(ranges::begin(r), N)</code>, and
    <code>unchecked_drop</code> is somewhat equivalent to
    <code>subrange(ranges::next(ranges::begin(r), N), ranges::end(r))</code>, these are not fully replaceable
    due to certain limitations.
  </p>
  <p>
    The main problem is that both require manually extracting iterators of the range, and such iterator-based
    approach causes dangling when applied to rvalue ranges.
  </p>
  <p>
    Since we've already assumed that the range is large enough, there is no need to consider out-of-bounds cases in the
    implementation,
    which means that <code>unchecked_<i>meow</i>_view</code> is just a
    simplified version of <code><i>meow</i>_view</code>, introducing them doesn't bring much complexity as the
    <code><i>meow</i>_view</code> already not that complicated.
  </p>
  <h3>
    Should we specialized for return types?</h3>
  <p>Currently, both <code>take</code> and <code>drop</code>
    have optimized the return type. When they take an object of range type <code>empty_view</code>,
    <code>span</code>, <code>string_view</code>, <code>subrange</code>, <code>iota_view</code>, and
    <code>repeat_view</code>, it will return an object of the same type to reduce template instantiation. There is no
    reason not to apply similar optimizations to new ones.
    Noted that the author removed the specialization for <code>empty_view</code> as the value of <code>N</code> can only
    be
    <code>0</code>.
  </p>
  <p>In addition, <code>views::unchecked_<i>meow</i></code>
    applied to infinite ranges now can downgrade to finite ranges.
    For example, <code>views::iota(0) | views::unchecked_take(5)</code> will produce
    <code>views::iota(0, 5)</code>, and <code>views::iota(0) | views::unchecked_drop(5)</code> will produce
    <code>views::iota(5)</code>. This also applies to infinite <code>subrange</code>s, which can be considered as an
    improvement.
  </p>
  <a name="Implementation experience"></a>
  <h2>Implementation experience</h2>
  <p>The author implemented <code>views::unchecked_(take|drop)</code> based on libstdc++, see <a
      href="https://godbolt.org/z/TraEd54Ge">here</a>.</p>
  <a name="Benchmarks"></a>
  <h2>Benchmarks</h2>
  <p>
    One of the advantages of <code>views::unchecked_take</code> over <code>views::take</code> is that it always produces
    a sized range
    even if the original range is a non-size range such as
    <code>generator</code> or
    <code>istream_view</code>, this can further benefit the container construction downstream as size information
    implies the reserve size.
  </p>
  <p>In other words, <code>r | views::unchecked_take(N) | ranges::to&lt;C&gt;</code> would have better performance than
    <code>r | views::take(N) | ranges::to&lt;C&gt;</code>.
    The following table shows that for the input-only range, constructing a <code>vector</code> after applying
    <code>views::unchecked_take</code> is ~3.8 times faster than <code>views::take</code> in terms of Iterations
    (see <a href="https://godbolt.org/z/hbsffhz8r">here</a>):
  </p>
  <blockquote>
    <table border="1">
      <caption>Table &mdash; Performance comparison of constructing <code>vector</code> via
        <code>views::take</code>
        vs. <code>views::unchecked_take</code>
      </caption>
      <tr>
        <th>
          Benchmark
        </th>
        <th>
          Time
        </th>
        <th>
          CPU
        </th>
        <th>
          Iterations
        </th>
      </tr>

      <tr>
        <td>
          <code>construct_from_take&lt;std::vector&lt;int&gt;&gt;</code>
        </td>
        <td>
          17454461 ns
        </td>
        <td>
          9032396 ns
        </td>
        <td>
          59
        </td>
      </tr>

      <tr>
        <td>
          <code>construct_from_unchecked_take&lt;std::vector&lt;int&gt;&gt;</code>
        </td>
        <td>
          7671973 ns
        </td>
        <td>
          3164699 ns
        </td>
        <td>
          222
        </td>
      </tr>

    </table>
  </blockquote>

  <p>
    <code>views::unchecked_drop</code> also has better performance when applied to non-sized ranges,
    as the new <code>begin()</code> for all random-access ranges can be obtained in constant time by
    just calculating <code>r.begin() + N</code>.
    The following table shows that for non-sized null-terminated raw string, constructing a <code>string</code> after
    applying
    <code>views::unchecked_drop</code> is ~1.6 times faster than <code>views::drop</code> in terms of Iterations
    (see <a href="https://godbolt.org/z/MEWaoM9nb">here</a>):
  </p>
  <blockquote>
    <table border="1">
      <caption>Table &mdash; Performance comparison of constructing <code>string</code> via
        <code>views::unchecked_drop</code>
        vs. <code>views::drop</code>
      </caption>
      <tr>
        <th>
          Benchmark
        </th>
        <th>
          Time
        </th>
        <th>
          CPU
        </th>
        <th>
          Iterations
        </th>
      </tr>

      <tr>
        <td>
          <code>construct_from_drop&lt;std::string&gt;</code>
        </td>
        <td>
          9762106 ns
        </td>
        <td>
          5644617 ns
        </td>
        <td>
          121
        </td>
      </tr>

      <tr>
        <td>
          <code>construct_from_unchecked_drop&lt;std::string&gt;</code>
        </td>
        <td>
          6812259 ns
        </td>
        <td>
          4149667 ns
        </td>
        <td>
          192
        </td>
      </tr>

    </table>
  </blockquote>
  <a name="Proposed-change"></a>
  <h2>Proposed change</h2>
  <p>This wording is relative to <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/n4993.pdf">N4993</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_unchecked_take 2024XXL // <i>freestanding, also in</i> &lt;ranges&gt;</ins></pre>
            <pre><ins>#define __cpp_lib_ranges_unchecked_drop 2024XXL // <i>freestanding, also in</i> &lt;ranges&gt;</ins></pre>
          </blockquote>
        </li>

        <li>
          <p>Modify 26.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> take = <i>unspecified</i>; }              // <i>freestanding</i>

<ins>  // <i>[range.unchecked.take]</a>, unchecked take view</i>
  template&lt;view&gt; class unchecked_take_view;                                         // <i>freestanding</i>

  template&lt;class T&gt;
    constexpr bool enable_borrowed_range&lt;unchecked_take_view&lt;T&gt;&gt; =                  // <i>freestanding</i>
      enable_borrowed_range&lt;T&gt;;

  namespace views { inline constexpr <i>unspecified</i> unchecked_take = <i>unspecified</i>; }    // <i>freestanding</i></ins>
  [&hellip;]
  namespace views { inline constexpr <i>unspecified</i> drop = <i>unspecified</i>; }              // <i>freestanding</i>

<ins>  // <i>[range.unchecked.drop]</a>, unchecked drop view</i>
  template&lt;view V&gt; class unchecked_drop_view;                                       // <i>freestanding</i>

  template&lt;class T&gt;
    constexpr bool enable_borrowed_range&lt;unchecked_drop_view&lt;T&gt;&gt; =                  // <i>freestanding</i>
      enable_borrowed_range&lt;T&gt;;

  namespace views { inline constexpr <i>unspecified</i> unchecked_drop = <i>unspecified</i>; }    // <i>freestanding</i></ins>
  [&hellip;]
}
        </pre>
          </blockquote>
        </li>
        <li>
          <p>Add <b>26.7.? Unchecked take view [range.unchecked.take]</b></a> after 26.7.10 <a
              href="https://eel.is/c++draft/range.take">[range.take]</a> as indicated:</p>
          <p>[26.7.?.1] Overview [range.unchecked.take.overview]</p>
          <p>
            -1- <code>unchecked_take_view</code> produces a view of the first <code><i>N</i></code> elements from
            another
            view.
            The behavior is undefined if the adapted view contains fewer than <code><i>N</i></code> elements.
          </p>
          <p>
            -2- The name <code>views::unchecked_take</code> denotes a range adaptor object (26.7.2 <a
              href="https://eel.is/c++draft/range.adaptor.object">[range.adaptor.object]</a>).
            Let <code>E</code> and <code>F</code> be expressions, let <code>T</code> be
            <code>remove_cvref_t&lt;decltype((E))&gt;</code>,
            and let <code>D</code> be <code>range_difference_t&lt;decltype((E))&gt;</code>.
            If <code>decltype((F))</code> does not model <code>convertible_to&lt;D&gt;</code>,
            <code>views::unchecked_take(E, F)</code>
            is ill-formed. Otherwise, the expression <code>views::unchecked_take(E, F)</code> is expression-equivalent
            to:
          </p>
          <ol style="list-style-type: none">
            <li>
              <p>(2.1) &mdash; If <code>T</code> models <code>random_access_range</code>
                and is a specialization of <code>span</code> (24.7.2.2 <a
                  href="https://eel.is/c++draft/views.span">[views.span]</a>),
                <code>basic_string_view</code> (23.3 <a href="https://eel.is/c++draft/string.view">[string.view]</a>),
                or
                <code>ranges::subrange</code>
                (26.5.4 <a href="https://eel.is/c++draft/range.subrange">[range.subrange]</a>), then <code>U(ranges::begin(E), ranges::begin(E) + 
                  static_cast&lt;D&gt;(F))</code>, except that <code>E</code> is evaluated only once,
                where <code>U</code> is a type determined as follows:
              </p>
              <ol style="list-style-type: none">
                <li>
                  <p>(2.1.1) &mdash; if <code>T</code> is a specialization of <code>span</code>, then <code>U</code> is
                    <code>span&lt;typename T::element_type&gt;</code>;
                  </p>
                </li>
                <li>
                  <p>(2.1.2) &mdash; otherwise, if <code>T</code> is a specialization of <code>basic_string_view</code>,
                    then
                    <code>U</code> is <code>T</code>;
                  </p>
                </li>
                <li>
                  <p>(2.1.3) &mdash; otherwise, <code>T</code> is a specialization of <code>subrange</code>,
                    and <code>U</code> is <code>subrange&lt;iterator_t&lt;T&gt;&gt;</code>;</p>
                </li>
              </ol>
            </li>
            <li>
              <p>(2.2) &mdash; otherwise, if <code>T</code> is a specialization of <code>iota_view</code>
                (26.6.4.2 <a href="https://eel.is/c++draft/range.iota.view">[range.iota.view]</a>) that models
                <code>random_access_range</code>, then
                <code>iota_view(*ranges::begin(E), *(ranges::begin(E) + static_cast&lt;D&gt;(F)))</code>,
                except that <code>E</code> is evaluated only once.
              </p>
            </li>
            <li>
              <p>(2.3) &mdash; Otherwise, if <code>T</code> is a specialization of <code>repeat_view</code>
                (26.6.5.2 <a href="https://eel.is/c++draft/range.repeat.view">[range.repeat.view]</a>), then
                <code>views::repeat(*E.<i>value_</i>, static_cast&lt;D&gt;(F))</code>.
              </p>
            </li>
            <li>
              <p>(2.4) &mdash; Otherwise, <code>unchecked_take_view(E, F)</code>.</p>
            </li>
          </ol>

          <p>-3- [<i>Example 1</i>:</p>
          <pre>
  auto ints = views::iota(0) | views::unchecked_take(10);
  for (auto i : ints | views::reverse) {
    cout &lt;&lt; i &lt;&lt; ' ';  // <i>prints</i> 9 8 7 6 5 4 3 2 1 0
  }</pre>
          — <i>end example]</i>
          <p>[26.7.?.2] Class template <code>unchecked_take_view</code> [range.unchecked.take.view]</p>
          <pre>
  namespace std::ranges {
    template&lt;view V&gt;
    class unchecked_take_view : public view_interface&lt;unchecked_take_view&lt;V&gt;&gt; {
    private:
      V <i>base_</i> = V();                                      // <i>exposition only</i>
      range_difference_t&lt;V&gt; <i>count_</i> = 0;                   // <i>exposition only</i>

    public:
      unchecked_take_view() requires default_initializable&lt;V&gt; = default;
      constexpr explicit unchecked_take_view(V base, range_difference_t&lt;V&gt; count);
  
      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;) {
        if constexpr (random_access_range&lt;V&gt;)
          return ranges::begin(<i>base_</i>);
        else
          return counted_iterator(ranges::begin(<i>base_</i>), <i>count_</i>);
      }
  
      constexpr auto begin() const requires range&lt;const V&gt; {
        if constexpr (random_access_range&lt;const V&gt;)
          return ranges::begin(<i>base_</i>);
        else
          return counted_iterator(ranges::begin(<i>base_</i>), <i>count_</i>);
      }
  
      constexpr auto end() requires (!<i>simple-view</i>&lt;V&gt;) {
        if constexpr (random_access_range&lt;V&gt;)
          return ranges::begin(<i>base_</i>) + <i>count_</i>;
        else
          return default_sentinel;
      }
  
      constexpr auto end() const requires range&lt;const V&gt; {
        if constexpr (random_access_range&lt;const V&gt;)
          return ranges::begin(<i>base_</i>) + <i>count_</i>;
        else
          return default_sentinel;
      }
  
      constexpr auto size() const noexcept { return <i>to-unsigned-like</i>(<i>count_</i>); }
    };
  
    template&lt;class R&gt;
      unchecked_take_view(R&amp;&amp;, range_difference_t&lt;R&gt;)
        -&gt; unchecked_take_view&lt;views::all_t&lt;R&gt;&gt;;
  }
</pre>
          <pre>constexpr explicit unchecked_take_view(V base, range_difference_t&lt;V&gt; count);</pre>
          <blockquote>
            <p>-1- <i>Preconditions</i>: <code>count &gt;= 0</code> is <code>true</code> and <code>base</code> has at least
              <code>count</code> elements.
            </p>
            <p>-2- <i>Effects</i>: Initializes <code><i>base_</i></code> with <code>std::move(base)</code> and
              <code><i>count_</i></code> with <code>count</code>.
            </p>
          </blockquote>
        <li>
          <p>Add <b>26.7.? Unchecked drop view [range.unchecked.drop]</b></a> after 26.7.12 <a
              href="https://eel.is/c++draft/range.drop">[range.drop]</a> as indicated:</p>
          <p>[26.7.?.1] Overview [range.unchecked.drop.overview]</p>
          <p>
            -1- <code>unchecked_drop_view</code> produces a view excluding the first <code><i>N</i></code> elements from
            another view.
            The behavior is undefined if the adapted view contains fewer than <code><i>N</i></code> elements.
          </p>
          <p>
            -2- The name <code>views::unchecked_drop</code> denotes a range adaptor object (26.7.2 <a
              href="https://eel.is/c++draft/range.adaptor.object">[range.adaptor.object]</a>).
            Let <code>E</code> and <code>F</code> be expressions, let <code>T</code> be
            <code>remove_cvref_t&lt;decltype((E))&gt;</code>,
            and let <code>D</code> be <code>range_difference_t&lt;decltype((E))&gt;</code>.
            If <code>decltype((F))</code> does not model <code>convertible_to&lt;D&gt;</code>,
            <code>views::unchecked_drop(E, F)</code>
            is ill-formed. Otherwise, the expression <code>views::unchecked_drop(E, F)</code> is expression-equivalent
            to:
          </p>
          <ol style="list-style-type: none">
            <li>
              <p>(2.1) &mdash; If <code>T</code> models <code>random_access_range</code>
                and is
              </p>
              <ol style="list-style-type: none">
                <li>
                  <p>(2.1.1) &mdash; a specialization of <code>span</code> (24.7.2.2 <a
                      href="https://eel.is/c++draft/views.span">[views.span]</a>),</p>
                </li>
                <li>
                  <p>(2.1.2) &mdash; a specialization of <code>basic_string_view</code> (23.3 <a
                      href="https://eel.is/c++draft/string.view">[string.view]</a>), </p>
                </li>
                <li>
                  <p>(2.1.3) &mdash; a specialization of <code>iota_view</code> (26.6.4.2 <a
                      href="https://eel.is/c++draft/range.iota.view">[range.iota.view]</a>), or</p>
                </li>
                <li>
                  <p>(2.1.4) &mdash; a specialization of <code>subrange</code> (26.5.4 <a
                      href="https://eel.is/c++draft/range.subrange">[range.subrange]</a>)
                    where <code>T::<i>StoreSize</i></code> is <code>false</code>,</p>
                </li>
              </ol>
              <p>then <code>U(ranges::begin(E) + static_cast&lt;D&gt;(F), ranges::end(E))</code>,
                except that <code>E</code> is evaluated only once, where <code>U</code> is <code>span&lt;typename
                  T::element_type&gt;</code>
                if <code>T</code> is a specialization of <code>span</code> and <code>T</code> otherwise.
              </p>
            </li>
            <li>
              <p>(2.2) &mdash; Otherwise, if <code>T</code> is a specialization of <code>subrange</code>
                (26.5.4 <a href="https://eel.is/c++draft/range.subrange">[range.subrange]</a>) that models
                <code>random_access_range</code> and <code>sized_range</code>, then
                <code>T(ranges::begin(E) + static_cast&lt;D&gt;(F), ranges::end(E),
                  <i>to-unsigned-like</i>(ranges::distance(E) - static_cast&lt;D&gt;(F)))</code>,
                except that <code>E</code> and <code>F</code> are each evaluated only once.
              </p>
            </li>
            <li>
              <p>(2.3) &mdash; Otherwise, if <code>T</code> is a specialization of <code>repeat_view</code> (26.6.5.2 <a
                  href="https://eel.is/c++draft/range.repeat.view">[range.repeat.view]</a>):
              </p>
              <ol style="list-style-type: none">
                <li>
                  <p>(2.3.1) &mdash; if <code>T</code> models <code>sized_range</code>, then
                  <pre>  views::repeat(*E.<i>value_</i>, ranges::distance(E) - static_cast&lt;D&gt;(F))</pre>
                  except that <code>E</code> is evaluated only once;</p>
                </li>
                <li>
                  <p>(2.3.2) &mdash; otherwise, <code>((void)F, <i>decay-copy</i>(E))</code>, except that the
                    evaluations of
                    <code>E</code> and <code>F</code> are indeterminately sequenced.
                  </p>
                </li>
              </ol>
            </li>
            <li>
              <p>(2.4) &mdash; Otherwise, <code>unchecked_drop_view(E, F)</code>.
              </p>
            </li>
            </p>
            <p>-3- [<i>Example 1</i>:</p>
            <pre>
  vector&lt;int&gt; v{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
  for (int i : is | views::unchecked_drop(6) | views::unchecked_take(3)) {
    cout &lt;&lt; i &lt;&lt; ' '; // <i>prints</i> 6 7 8
  }</pre>
            — <i>end example]</i>
            <p>[26.7.?.2] Class template <code>unchecked_drop_view</code> [range.unchecked.drop.view]</p>
            <pre>
    namespace std::ranges {
      template&lt;view V&gt;
      class unchecked_drop_view : public view_interface&lt;unchecked_drop_view&lt;V&gt;&gt; {
      public:
        unchecked_drop_view() requires default_initializable&lt;V&gt; = default;
        constexpr explicit unchecked_drop_view(V base, range_difference_t&lt;V&gt; count);
    
        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;));
        constexpr auto begin() const requires random_access_range&lt;const V&gt;;
    
        constexpr auto end() requires (!<i>simple-view</i>&lt;V&gt;)
        { return ranges::end(<i>base_</i>); }
    
        constexpr auto end() const requires range&lt;const V&gt;
        { return ranges::end(<i>base_</i>); }
    
        constexpr auto size() requires sized_range&lt;V&gt;
        { return ranges::size(<i>base_</i>) - static_cast&lt;range_size_t&lt;V&gt;&gt;(<i>count_</i>); }
    
        constexpr auto size() const requires sized_range&lt;const V&gt;
        { return ranges::size(<i>base_</i>) - static_cast&lt;range_size_t&lt;const V&gt;&gt;(<i>count_</i>); }
  
      private:
        V <i>base_</i> = V();                                      // <i>exposition only</i>
        range_difference_t&lt;V&gt; <i>count_</i> = 0;                   // <i>exposition only</i>
      };
    
      template&lt;class R&gt;
        unchecked_drop_view(R&amp;&amp;, range_difference_t&lt;R&gt;)
          -&gt; unchecked_drop_view&lt;views::all_t&lt;R&gt;&gt;;
    }
  </pre>
            <pre>constexpr explicit unchecked_drop_view(V base, range_difference_t&lt;V&gt; count);</pre>
            <blockquote>
              <p>-1- <i>Preconditions</i>: <code>count &gt;= 0</code> is <code>true</code> and <code>base</code> has at least
                <code>count</code> elements.
              </p>
              <p>-2- <i>Effects</i>: Initializes <code><i>base_</i></code> with <code>std::move(base)</code> and
                <code><i>count_</i></code> with <code>count</code>.
              </p>
            </blockquote>
            <pre>constexpr auto begin() requires (!(<i>simple-view</i>&lt;V&gt; &amp;&amp; random_access_range&lt;const V&gt;));
constexpr auto begin() const requires random_access_range&lt;const V&gt;</pre>
            <blockquote>
              <p>
                -3- <i>Returns</i>: <code>ranges::next(ranges::begin(<i>base_</i>), <i>count_</i>)</code>.
              </p>
              <p>
                -4- <i>Remarks</i>:
                In order to provide the amortized constant-time complexity required by the <code>range</code> concept
                when <code>unchecked_drop_view</code> models <code>forward_range</code>, the first overload caches the
                result within the
                <code>unchecked_drop_view</code> for use on subsequent calls.
              </p>
              [<i>Note 1</i>: Without this, applying a <code>reverse_view</code> over a <code>unchecked_drop_view</code>
              would have
              quadratic iteration complexity. &mdash; <i>end note</i>]
              </p>
            </blockquote>
            <blockquote>
            </blockquote>
            </blockquote>
            </blockquote>
            </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>