

<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>Better <code>mdspan</code>'s CTAD</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>P3029R0</td></tr>
  <tr><td>Date</td><td>2023-10-24</td></tr>
  <tr><td>Audience</td><td>LEWG</td></tr>
  <tr><td>Reply-to</td><td>Hewill Kang &lt;hewillk@gmail.com&gt;</td></tr>
  </tbody></table><hr>
  <h1>Better <code>mdspan</code>'s CTAD</h1>
  <ul>
   <li>
   <ul>
    <li>Abstract</li>
    <li>Revision history</li>
    <li>Discussion</li>
    <li>Design</li>
    <li>Proposed change</li>
    <li>References</li>
   </ul>
   </li>
  </ul>
  <a name="Abstract"></a>
  <h2>Abstract</h2>
  <p>This paper makes <code>span</code> and <code>mdspan</code>' CTAD <code>integral_constant</code>-aware to deduce more efficient types.</p>
  
  <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, <code>mdspan</code>'s most common pointer-indices CTAD have the following definitions:
  </p>
<pre>
  template&lt;class ElementType, class... Integrals&gt;
    requires((is_convertible_v&lt;Integrals, size_t&gt; && ...) && sizeof...(Integrals) > 0)
    explicit mdspan(ElementType*, Integrals...)
      -> mdspan&lt;ElementType, dextents&lt;size_t, sizeof...(Integrals)&gt;&gt;;
</pre>
<p>
  Since the <code>Extents</code> template parameter of <code>mdspan</code> is explicitly specified as <code>dextents</code>, the deduced type always has dynamic extent, 
  even if we pass a compile-time constant:
</p>
<pre>
  mdspan ms (p, 3, 4, 5); // <span style="color:red;font-weight:bolder">mdspan&lt;int, extents&lt;size_t, dynamic_extent, dynamic_extent, dynamic_extent&gt;&gt;</span>
  mdspan ms2(p, 3, integral_constant&lt;size_t, 4&gt;{}, 5);                              // <span style="color:red;font-weight:bolder">ditto</span>
  mdspan ms3(p, integral_constant&lt;size_t, 3&gt;{}, 4, integral_constant&lt;size_t, 5&gt;{}); // <span style="color:red;font-weight:bolder">ditto</span>
</pre>
<p>
  This result feels wrong to me. If <code>integral_constant</code> represents a compile-time constant, why don't we take advantage of it and make it reflect the extent of <code>mdspan</code>, since we already did this with <code>strided_slice</code>?
</p><p>The author believes that instead of setting static extents by additionally creating <code>extents</code>:
</p>
<pre>
  mdspan ms2(p, extents&lt;size_t, dynamic_extent, 4, dynamic_extent&gt;(3, 5)); // <span style="color:red;font-weight:bolder">mdspan&lt;int, extents&lt;size_t, dynamic_extent, 4, dynamic_extent&gt;&gt;</span>
  mdspan ms3(p, extents&lt;size_t, 3, dynamic_extent, 5&gt;(4));                 // <span style="color:red;font-weight:bolder">mdspan&lt;int, extents&lt;size_t, 3, dynamic_extent, 5&gt;&gt;</span>
</pre>
<p>
  It would be better to automatically make the first example equivalent based on whether the argument is a compile-time constant.</p>
  <p>This brings the following benefits:</p>
</p>
<ol>
<li>
It enables defining dynamic or static extents with the similar form and intuitively reflects the extent type.
</li>
<li>
  It's less error-prone as we don't need to calculate how many <code>dynamic_extents</code> 
  there are to pass in the correct number of arguments. The compiler already handle it for us.
</li>
<li>
  It makes defining <code>mdspan</code> more concise, an <code>mdspan</code> with mixed extents now can be spelled as 
  <code>mdspan(c_&lt;3&gt;{}, 4, c_&lt;5&gt;{})</code> (benefit from <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2781r3.html">P2781</a> <code>std::constexpr_v</code>).
</li>
</ol>
  
  <a name="Design"></a>
  <h2>Design</h2>
  <p>
    <h3><code>span</code>'s CTAD</h3>
    <p>
      The paper also applies to <code>span</code>' CTAD for consistency.
      In other words, <code>span(p, c_&lt;5&gt;{})</code> will be deduced as <code>span&lt;int, 5&gt;</code>.
      The author doesn't think this is a big issue as it's relative intuitive and generally users don't often write this way.
  </p>
  <p>
    <h3><code>mdspan</code>'s CTAD</h3>
    <p>Given that <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2630r4.html">P2630</a> <code>std::submdspan</code> already introduced <code><i>integral-constant-like</i></code> for checking <code>integral_constant</code>-like type, 
      this paper reuses it to detect whether <code>mdspan</code>'s arguments are a compile-time constant. This also applies to <code>extents</code>.</p>
  </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/n4917.pdf">N4917</a>.</p>
  </div>
  <div>
  <ol><ol>
<li><p>Edit 24.7.2.1 <a href="https://eel.is/c++draft/span.syn">[span.syn]</a> as indicated:</p>
  <pre>
namespace std {
  // <i>constants</i>
  inline constexpr size_t dynamic_extent = numeric_limits&lt;size_t&gt;::max();

<ins>  template&lt;class T&gt;
    concept <i>integral-constant-like</i> =        // <i>exposition only</i>
      is_integral_v&lt;decltype(T::value)&gt; &&
      !is_same_v&lt;bool, remove_const_t&lt;decltype(T::value)&gt;&gt; &&
      convertible_to&lt;T, decltype(T::value)&gt; &&
      equality_comparable_with&lt;T, decltype(T::value)&gt; &&
      bool_constant&lt;T() == T::value&gt;::value &&
      bool_constant&lt;static_cast&lt;decltype(T::value)&gt;(T()) == T::value&gt;::value;</ins>

  <ins>template&lt;class T&gt;
    constexpr size_t <i>maybe-static-ext</i> = dynamic_extent;        // <i>exposition only</i>
  template&lt;<i>integral-constant-like</i> T&gt;
    constexpr size_t <i>maybe-static-ext</i>&lt;T&gt; = static_cast&lt;size_t&gt;(T::value);</ins>

  // <i><a href="https://eel.is/c++draft/views.span">[views.span]</a>, class template span</i>
  template&lt;class ElementType, size_t Extent = dynamic_extent&gt;
    class span;
  [&hellip;]
}
  </pre>
  </li>
  <li>
  <p>Edit 24.7.2.2.1 <a href="https://eel.is/c++draft/span.overview">[span.overview]</a> as indicated:</p>
  <p>-2- All member functions of span have constant time complexity.</p>
  <pre>
namespace std {
  template&lt;class ElementType, size_t Extent = dynamic_extent&gt;
  class span {
    [&hellip;]
  };

  template&lt;class It, class EndOrSize&gt;
    span(It, EndOrSize) -> span&lt;remove_reference_t&lt;iter_reference_t&lt;It&gt&gt, <ins><i>maybe-static-ext</i>&lt;EndOrSize&gt;</ins>&gt;;
  [&hellip;]
}
</pre>
</li>
<li>
<p>Edit 24.7.2.2.3 <a href="https://eel.is/c++draft/span.deduct">[span.deduct]</a> as indicated:</p>
<pre>
template&lt;class It, class EndOrSize&gt;
  span(It, EndOrSize) -> span&lt;remove_reference_t&lt;iter_reference_t&lt;It&gt&gt, <ins><i>maybe-static-ext</i>&lt;EndOrSize&gt;</ins>&gt;;
</pre>
<p>-1- <i>Constraints</i>: <code>It</code> satisfies <code>contiguous_iterator</code>.</p>
</li>
<li>
<p>Edit 24.7.3.3.3 <a href="https://eel.is/c++draft/mdspan.extents.cons">[mdspan.extents.cons]</a> as indicated:</p>
<pre>
template&lt;class... Integrals&gt;
  explicit extents(Integrals...) -> <i>see below</i>;
</pre>
<p>-1- <i>Constraints</i>: <code>(is_convertible_v&lt;Integrals, size_t&gt; && ...)</code> is <code>true</code>.</p>
<p>-2- <i>Remarks</i>: The deduced type is <code><del>d</del>extents&lt;size_t, <ins><i>maybe-static-ext</i>&lt;Integrals&gt;...</ins><del>sizeof...(Integrals)</del>&gt;</code>.</p>
  </li>
<li>
  <p>Edit 24.7.3.2 <a href="https://eel.is/c++draft/mdspan.syn">[mdspan.syn]</a> as indicated:</p>
  <pre>
namespace std {
  [&hellip;]
  <del>template&lt;class T&gt;
    concept <i>integral-constant-like</i> =        // <i>exposition only</i>
      is_integral_v&lt;decltype(T::value)&gt; &&
      !is_same_v&lt;bool, remove_const_t&lt;decltype(T::value)&gt;&gt; &&
      convertible_to&lt;T, decltype(T::value)&gt; &&
      equality_comparable_with&lt;T, decltype(T::value)&gt; &&
      bool_constant&lt;T() == T::value&gt;::value &&
      bool_constant&lt;static_cast&lt;decltype(T::value)&gt;(T()) == T::value&gt;::value;</del>
  [&hellip;]
}
  </pre>
    </li>
<li>
  <p>Edit 24.7.3.6.1 <a href="https://eel.is/c++draft/mdspan.mdspan.overview">[mdspan.mdspan.overview]</a> as indicated:</p>
  <p>-1- <code>mdspan</code> is a view of a multidimensional array of elements.</p>
  <pre>
namespace std {
  template&lt;class ElementType, class Extents, class LayoutPolicy = layout_right,
           class AccessorPolicy = default_accessor&lt;ElementType&gt;&gt;
  class mdspan {
    [&hellip;]
  };

  template&lt;class CArray&gt;
    requires(is_array_v&lt;CArray&gt; && rank_v&lt;CArray&gt; == 1)
    mdspan(CArray&)
      -> mdspan&lt;remove_all_extents_t&lt;CArray&gt;, extents&lt;size_t, extent_v&lt;CArray, 0&gt;&gt;&gt;;

  template&lt;class Pointer&gt;
    requires(is_pointer_v&lt;remove_reference_t&lt;Pointer&gt;&gt;)
    mdspan(Pointer&&)
      -> mdspan&lt;remove_pointer_t&lt;remove_reference_t&lt;Pointer&gt;&gt;, extents&lt;size_t&gt;&gt;;

  template&lt;class ElementType, class... Integrals&gt;
    requires((is_convertible_v&lt;Integrals, size_t&gt; && ...) && sizeof...(Integrals) > 0)
    explicit mdspan(ElementType*, Integrals...)
      -> mdspan&lt;ElementType, <del>d</del>extents&lt;size_t, <ins><i>maybe-static-ext</i>&lt;Integrals&gt;...</ins><del>sizeof...(Integrals)</del>&gt;&gt;;

  [&hellip;]
}
  </pre>
    </li>
  </ol>
  </ol>
  </div>

  <a name="References"></a>
  <h2>References</h2>
  
  <dd>
    <dt id="biblio-p2321">[P2630R4]
    <dd>Christian Trott. <code>Submdspan</code>. URL: <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2630r4.html">https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2630r4.html</a>
    <dt id="biblio-p2321">[P2781R3]
    <dd>Matthias Kretz. <code>std::constexpr_v</code>. URL: <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2781r3.html">https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2781r3.html</a>
  </body></html>