<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>P3216R0</td>
      </tr>
      <tr>
        <td>Date</td>
        <td>2024-04-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 in <a
      href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2760r1.html">P2760</a>:
    <code>views::slice</code> to improve the C++26 ranges facilities,
    which is a simple composition of <code>views::take</code> and <code>views::drop</code>.
  <p>
    Note that this is the first range adaptor in the standard that accepts <i>two</i> arguments, namely
    <code><i>begin_index</i></code> and <code><i>end_index</i></code> to specify the slice interval
    [<code><i>begin_index</i></code>,
    <code><i>end_index</i></code>).
  </p>
  </p>
  <a name="Revision history"></a>
  <h2>Revision history</h2>
  <p>
  <h3>R0</h3>
  <p>Initial revision.</p>
  </p>
  </ol>
  </p>
  <a name="Discussion"></a>
  <h2>Discussion</h2>
  <p>
    Slice is very common operation in other languages, there is also a similar view in range/v3,
    <code>views::slice</code>, which returns a <i>slice</i> of the original range bounded by two indices.
  </p>
  <p>Unfortunately, due to its relatively minor importance at the time, <code>slice</code> was only classified as Tier 3
    in <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2214r2.html">P2214</a>.
    However, as the three sliding window brothers: <code>chunk</code>, <code>slide</code>, and <code>stride</code>
    entered C++23, <code>slice</code>,
    which was once neglected, entered the public eye again.</p>
  <p>
    Although it is classified in the <code>take</code>/<code>/drop</code> family in <a
      href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2760r1.html">P2760</a>, the author believes that
    it can also be regarded as a window-related utility to a certain extent because it <a
      href="https://stackoverflow.com/a/56638545/11638718">cooperates well</a> with the slding family. In addition,
    <code>slide</code> can provide a more comfortable and generic way to obtain a <i>slice</i> of a range rather
    than
    <code>subrange</code> and
    <code>counted</code>, while the latter two still have certain limitations when applied to
    non-<code>forward_range</code> or non-<code>sized_range</code>s.
  </p>
  <p>Given the above, the author believes that it is time to bring <code>slice</code> into C++26.</p>
  </ol>
  <a name="Design"></a>
  <h2>Design</h2>
  <p>
  <h3>Should we introduce a new <code>slice_view</code> class?</h3>
  <p>Nope.</p>
  <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>
    This means that <code>slice(M, N)</code> can simply be a trivial alias of the latter two,
    and author believes that such a design has fully accommodated the current desires.</p>
  <p>First, the implementation of <code>slice</code> involves advancing to the beginning of the slice and calculating
    the end, which perfectly matches matches what <code>drop</code> and <code>take</code> is currently doing. If this is
    the case,
    the author sees no reason not to compose them.</p>
  <p>Second, those two have certain specializations for return types in order to avoid template explosion,
    composing them means directly inheriting existing optimizations. In other words, <code>span{v} |
    slice(1, 10) | slice(3, 5)</code> will still produce a <code>span</code> containing the proper slice. And if
    there are any improvements for <code>drop</code> and <code>take</code> in the future, <code>slice</code> can
    automatically benefit.</p>
  <p>It is worth noting that since <code>drop</code> and <code>take</code> support output ranges, which makes
    <code>slice</code> also
    support output ranges. There is no worthwhile reason for not supporting it.
  </p>
  <h3>Should the second argument be <i>size</i>?</h3>
  <p>Nope.</p>
  <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 since this is intuitive and consistent with
    other language syntaxes
    such as Python: <code>a[1:3]</code> and Rust: <code>&a[1..3]</code>, etc.</p>
  <h3>What if out of range or <code><i>end_index</i></code> is smaller than <code><i>begin_index</i></code>?</h3>
  <p>
    Since <code>slice</code> is a composition of <code>drop</code> and <code>take</code>, it also inherits their
    handling out-of-range indexes. That is to say,
    <code>iota(0, 5) | slice(3, 9)</code> will get <code>iota(3, 5)</code> and <code>iota(0, 5) | slice(7, 9)</code>
    will get an empty range. This is fine because it does conform to current standard behavior.
    Otherwise, what we need is probably a <code>slice_exactly</code> (via <code>drop_exactly</code> and
    <code>take_exactly</code>?)
  </p>
  <p>
    Another thing to note is that both <code>drop_view</code> and <code>take_view</code> have the <i>Preconditions</i>
    that <code>count >= 0</code>, which makes <code>slice(r, -1, 1)</code> or <code>slice(r, 2, 1)</code> UB if it
    eventually
    constructs <code>drop_view(r, -1)</code> or <code>take_view(r, -1)</code>; if it goes into specialized branches,
    then
    constructors of other views such as <code>iota_view</code> or <code>repeat_view</code> also have similar
    <i>Preconditions</i> to guarantee valid ranges.
  </p>
  <h3>Do we need to support special variable <code><i>end</i></code>?</h3>
  <p>
    Nope.</p>
  <p>
    Although range/v3 supports a special variable <code><i>end</i></code> for universally denoting the end index of a
    range,
    making it possible
    to spell it like <code>views::slice(M, <i>end</i> - N)</code>, the author believes that such use case is
    too specific and may become less and less necessary with the
    subsequent introduction of <code>drop_last</code>.</p>
  <h3>Should we provide a variant of <code>slice(1, 10, <i>stride</i>)</code>?</h3>
  <p>Nope.</p>
  <p>
    Other languages such as Python also support stride as the third parameter of slice such as
    <code>[1:10:3]</code>, which can be done by declaring <code>slice(M, N, P) as slice(M, N) | stride(P)</code>.
    However, the authors do not
    see much value in providing this variant.
  </p>
  <p>First, <code>slice(M, N) | stride(P)</code> can express the meaning of the third argument <code>P</code> more
    explicitly than <code>slice(M, N, P)</code>. In addition, <code>views::slice(M, N, P)</code> may be confused with
    similarly named classes in the standard, such as <code>std::slice</code> and <code>std::strided_slice</code>,
    whose second parameter has completely different meanings, representing <i>size</i> and <i>extent</i> respectively
    instead of
    ending index. There is really no need to introduce another layer of confusion.</p>
  <a name="Implementation experience"></a>
  <h2>Implementation experience</h2>
  <p>The author implemented <code>views::slice</code> based on libc++.
    The details are relatively simple in about 20 lines, see <a href="https://godbolt.org/z/rYeqa3vzM">here</a>.</p>
  <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/2023/n4971.pdf">N4971</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 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> drop_while = <i>unspecified</i>; }        // <i>freestanding</i>

<ins>  // <i>[range.slice]</a>, slice view</i>
  namespace views { inline constexpr <i>unspecified</i> slice = <i>unspecified</i>; }             // <i>freestanding</i></ins>
  [&hellip;]
}
        </pre>
          </blockquote>
        </li>
        <li>
          <p>Add <b>26.7.? Slice view [range.slice]</b></a> after 26.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 a view of elements from position <i>N</i> up to but not including position
            <i>M</i> of the original view. The resulting view is empty if the original view has fewer than
            (<i>N</i>+1) elements, or all elements starting from position <i>N</i> if it has fewer than <i>M</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 let <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
            <code>views::take(views::drop(E, F), static_cast&lt;D&gt;(G) - static_cast&lt;D&gt;(F))</code>, except that
            <code>F</code> is evaluated only once.
          </p>
          <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>
        </li>
        </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>
  <dt id="biblio-p2214">[P2214R2]
  <dd>Barry Revzin. A Plan for C++23 Ranges. URL: <a
      href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2214r2.html">https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2214r2.html</a>
</body>

</html>