<html>

<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title><code>view_interface::at</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>P3052R0</td>
      </tr>
      <tr>
        <td>Date</td>
        <td>2023-11-16</td>
      </tr>
      <tr>
        <td>Audience</td>
        <td>LEWG, SG23 (Safety and Security), SG9 (Ranges)
        </td>
      </tr>
      <tr>
        <td>Reply-to</td>
        <td>Hewill Kang &lt;hewillk@gmail.com&gt;</td>
      </tr>
    </tbody>
  </table>
  <hr>
  <h1><code>view_interface::at()</code></h1>
  <ul>
    <li>
      <ul>
        <li>Abstract</li>
        <li>Revision history</li>
        <li>Discussion</li>
        <li>Design</li>
        <li>Proposed change</li>
        <li>Acknowledgements</li>
      </ul>
    </li>
  </ul>
  <a name="Abstract"></a>
  <h2>Abstract</h2>
  <p>
    This paper provides the <code>at()</code> method to <code>ranges::view_interface</code> to provide a safe access
    method for the view class.
    <a name="Revision history"></a>
  <h2>Revision history</h2>
  <p>
  <h3>R0</h3>
  <p>Initial revision.</p>
  </p>
  <a name="Discussion"></a>
  <h2>Discussion</h2>
  <p>
    Currently, the committee adopted <a
      href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2821r4.html">P2821</a> in C++26, which adds a
    missing <code>at()</code> to <code>std::span</code> to consistent its API
    with other containers as well as <code>std::string_view</code>.
    Given that the two standard views <code>span</code> and <code>string_view</code> now have the <code>at()</code>
    method, The author thinks it's time to extend this further to generic views in <code>&lt;ranges&gt;</code>, which will bring:
  </p>
  <ol>
    <li>
      Consistency. <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1739r4.html">P1739</a> brings
      interaction between range adaptors and <code>span</code>/<code>string_view</code>,
      suggesting that it makes sense to maintain API consistency. Users do not need to worry about missing functionality
      when converting from the first two to view such as <code>subrange</code>.
    </li>

    <blockquote>
      <table border="1">
        <caption>Table &mdash; Standard range types and access APIs</caption>

        <tr>
          <th>
            Element access
          </th>
          <th>
            <tt>operator[]</tt>
          </th>
          <th>
            <tt>at</tt>
          </th>
          <th>
            <tt>front</tt>
          </th>
          <th>
            <tt>back</tt>
          </th>
          <th>
            <tt>data</tt>
          </th>
        </tr>

        <tr>
          <td>
            <tt>string</tt>/<tt>array</tt>/<tt>vector</tt>
          </td>
          <td>
            &#9989;
          </td>
          <td>
            &#9989;
          </td>
          <td>
            &#9989;
          </td>
          <td>
            &#9989;
          </td>
          <td>
            &#9989;
          </td>
        </tr>

        <tr>
          <td>
            <tt>string_view</tt>/<tt>span</tt>
          </td>
          <td>
            &#9989;
          </td>
          <td>
            &#9989;
          </td>
          <td>
            &#9989;
          </td>
          <td>
            &#9989;
          </td>
          <td>
            &#9989;
          </td>
        </tr>
        <tr>
          <td>
            <tt>ranges::<i>meow</i>_view</tt>
          </td>
          <td>
            &#9989;
          </td>
          <td>
            &#10060;
          </td>
          <td>
            &#9989;
          </td>
          <td>
            &#9989;
          </td>
          <td>
            &#9989;
          </td>
        </tr>

      </table>
    </blockquote>


    <li>
      Safety. Compared with standard containers, view classes do not provide any bounds checking, which makes indexing
      access unsafe and discourages third-party projects that focus on security.
      The <code>at()</code> method can be a turning point.
    </li>
  </ol>

  <a name="Design"></a>
  <h2>Design</h2>
  <p>
  <h3>How to make all view classes provide <code>at()</code> (if they can)?</h3>
  <p>
    All range factories/adaptors in <code>&lt;ranges&gt;</code> (including <code>std::generator</code>) are derived from
    <code>view_interface</code>,
    this is intended to synthesize more members through
    <code>view_interface</code> when they model a specific range concept.
  </p>
  <p>
    For example, a derived class that satisfies <code>forward_range</code> will have an available <code>front()</code> even if the
    implementation does not provide one.
    This makes it intuitive for users to spell something like <code>views::single(0).front()</code> to get the first
    element.
  </p>
  <p>
    In addition, thanks to <a href="https://cplusplus.github.io/LWG/issue3549">LWG 3549</a> reducing the
    size of range adaptors caused by unnecessary padding,
    views only need to inherit <code>view_interface</code> instead of <code>view_base</code> to be enabled.
    The author believes that any generic views should prefer to inherit from <code>view_interface</code>,
    such a feature of automatically inheriting functionality is very valuable.
  </p>
  <p>This is what <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2278r4.html">P2278</a> does by
    introducing <code>cbegin()</code>/<code>cend()</code> for views.
    Even though the derived class may not currently gain any benefit from <code>view_interface</code>,
    this does not mean that <code>view_interface</code> will not add new members or relax the constraints of some
    members,  just as LWG <a href="https://cplusplus.github.io/LWG/issue3715">3715</a> makes <code>input_range</code>s also have <code>empty()</code>.
  </p>
  <p>
    To sum up, the author believes that the implementation of <code>at()</code> can just by adding constrained members to
    <code>view_interface</code>.
  </p>
  </p>
  <p>
  <h3>When is <code>at()</code> provided?</h3>
  Since <code>at()</code> is a random access operation, the view type needs to model <code>random_access_range</code>;
  we also need to know the size of the range for boundary checking, which requires <code>sized_range</code>.
  </p>
  <p>
  <h3>What is the parameter type of the function?</h3>
  <p>
    There are three possible candidates for the parameter type of <code>at()</code>.
  </p>
  <p>The first is <code>range_size_t&lt;R&gt;</code>, which is the return type of <code>ranges::size</code> used to
    query range boundaries.
    However, since the signedness of this type is unspecified, and it is not closely related to the iterator's
    difference type which is involved in the implementation, the author does not consider it to be a suitable option.
  </p>
  <p>
  <p>
    So the question becomes, should the parameter type be signed i.e. <code>range_difference_t&lt;R&gt;</code>, or
    unsigned i.e. <code><i>make-unsigned-like-t</i>&lt;range_difference_t&lt;R&gt;&gt;</code>?
    The author believes that the former is a better choice, as it maintains a consistent interface with the
    <code>operator[]</code> and eliminates the need for the additional signedness conversion.
  </p>
  <h3>What are the conditions for boundary checking?</h3>
  <p>
    When the index value <code>n &lt; 0</code> or <code>n >= ranges::distance(r)</code>, it can be considered out of
    bounds.
  </p>
  <p>
    Although the underlying iterator-based formula implies that it works with negative signed integers, e.g.
    <code>subrange(v.begin() + 1, v.end())[-1]</code> legally points to the first element,
    the author believes that this kind of access cannot generally be regarded as a safe operation because
    <code>v.begin()</code> is indeed excluded from the originally intended scope,
    in which case throwing an exception is more likely to catch user errors.
  </p>
  <p>
    Since <code>ranges::distance(r)</code> is specified to return the signed value of <code>ranges::size(r)</code> when
    <code>r</code> is a <code>sized_range</code>,
    based on the consideration of reducing unnecessary type conversions in the previous discussion, the author prefers
    to use <code>ranges::distance(r)</code> in the condition.
  </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/2022/n4964.pdf">N4964</a>.
  </p>
  </div>
  <div>
    <ol>
      <ol>
        <li>
          <p>Add a new feature-test macro to 17.3.2 <a href="https://wg21.link/version.syn">[version.syn]</a>:</p>
          <blockquote>
            <pre>
<ins>#define __cpp_lib_view_interface_at 2023XXL // also in &lt;ranges&gt;</ins></pre>
          </blockquote>

        <li>
          <p>Modify 26.5.3 <a href="https://wg21.link/view.interface">[view.interface]</a> as indicated:</p>

          <blockquote>
            <pre>
namespace std::ranges {
  template&lt;class D&gt;
    requires is_class_v&lt;D&gt; &amp;&amp; same_as&lt;D, remove_cv_t&lt;D&gt;&gt;
  class view_interface {
    [&hellip;]
  public:
    [&hellip;]
    
    template&lt;random_access_range R = D&gt;
      constexpr decltype(auto) operator[](range_difference_t&lt;R&gt; n) {
        return ranges::begin(<i>derived</i>())[n];
      }
    template&lt;random_access_range R = const D&gt;
      constexpr decltype(auto) operator[](range_difference_t&lt;R&gt; n) const {
        return ranges::begin(<i>derived</i>())[n];
      }

    <ins>template&lt;random_access_range R = D&gt; requires sized_range&lt;R&gt;
      constexpr decltype(auto) at(range_difference_t&lt;R&gt; n);
    template&lt;random_access_range R = const D&gt; requires sized_range&lt;R&gt;
      constexpr decltype(auto) at(range_difference_t&lt;R&gt; n) const;</ins>
  };
}
</pre>
            [&hellip;]
            <p>
            <pre>
  <ins>template&lt;random_access_range R = D&gt; requires sized_range&lt;R&gt;
    constexpr decltype(auto) at(range_difference_t&lt;R&gt; n);
  template&lt;random_access_range R = const D&gt; requires sized_range&lt;R&gt;
    constexpr decltype(auto) at(range_difference_t&lt;R&gt; n) const;</ins>
</pre>
            </p>
            <p><ins>-?- <i>Returns</i>: <tt>operator[](n)</tt>.</ins></p>
            <p><ins>-?- <i>Throws</i>:
                <tt>out_of_range</tt> if <tt>n</tt> &lt; <tt>0</tt> or <tt>n</tt> &gt;=
                <tt>ranges::distance(<i>derived</i>())</tt>.</p>
          </blockquote>
        </li>
      </ol>
    </ol>
  </div>
  <a name="References"></a>
  <h2>References</h2>
  <dd>
  <dt id="biblio-p2821">[P2821R4]
  <dd>Jarrad J. Waterloo. <code>span.at()</code>. URL: <a
      href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2821r4.html">https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2821r4.html</a>
  <dt id="biblio-p1739">[P1739R4]
  <dd>Hannes Hauswedell. Avoid template bloat for <code>safe_range</code>s in combination with ‘subrange-y’ view
    adaptors. URL: <a
      href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1739r4.html">https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1739r4.html</a>
  <dt id="biblio-lwg3549">[LWG 3549]
  <dd>Tim Song. LWG 3549: <code>view_interface</code> is overspecified to derive from <code>view_base</code>. URL: <a
      href="https://cplusplus.github.io/LWG/issue3549">https://cplusplus.github.io/LWG/issue3549</a>
  <dt id="biblio-p2278">[P2278R4]
  <dd>Barry Revzin. <code>cbegin</code> should always return a constant iterator. URL: <a
      href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2278r4.html">https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2278r4.html</a>
  <dt id="biblio-lwg3715">[LWG 3715]
  <dd>Hewill Kang. LWG 3715: <code>view_interface::empty</code> is overconstrained. URL: <a
      href="https://cplusplus.github.io/LWG/issue3715">https://cplusplus.github.io/LWG/issue3715</a>
  </dd>

  <a name="Acknowledgements"></a>
  <h2>Acknowledgements</h2>
  <p>Thanks to Arthur O'Dwyer for sharing his valuable perspective on the maillist.</p>
</body>

</html>