
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>Add a conditional noexcept specification to std::apply</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>P2517R1</td></tr>
  <tr><td>Date</td><td>2022-07-09</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>Add a conditional <code>noexcept</code> specification to <code>std::apply</code></h1>
  <ul>
   <li>
   <ul>
    <li>Abstract</li>
    <li>Revision history</li>
    <li>Discussion</li>
    <li>Impact on the standard</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 to add a <code>noexcept</code>-specification to <code>std::apply</code>.</p>
  
  <a name="Revision history"></a>
  <h2>Revision history</h2>
  <p>
    <h3>R1</h3>
    <p>Added missing revision to header <code>&lt;tuple&gt;</code> synopsis. Aligned proposed changes with the latest draft.</p>
    <h3>R0</h3>
    <p>Initial revision.</p>
  </p>


  <a name="Discussion"></a>
  <h2>Discussion</h2>
  
  <p>With the introduction of the C++23 <code>zip</code> family, 
  <code>apply</code> is making a comeback and appeared in a lot of its function implementations.</p>
  
  <p>For example, in <a href="https://eel.is/c++draft/range.zip.transform#iterator-4">[range.zip.transform.iterator]</a>, 
  <code>zip_transform_view::iterator::operator*()</code>'s equivalent <em>Effect</em> is defined as:
      <pre><code>  
      return apply([&](const auto&... iters) -> decltype(auto) {
        return invoke(*parent_->fun_, *iters...);
      }, inner_.current_);
      </code></pre>
  
  which uses <code>apply</code> to extract the elements of the iterator <code>tuple</code> and forwards them into the callable.</p>
  
  <p>However, strictly speaking, this <code>operator*()</code> should not be done through <code>apply</code>. 
  The reason is that it still has a <code>noexcept(<em>see below</em>)</code> specification which is equivalent to 
  <code>noexcept(invoke(*parent_->fun_, *std::get&lt;Is&gt;(inner_.current_)...))</code>, 
  where <code>Is</code> is the pack <code>0</code>, <code>1</code>, …, <code>(sizeof...(Views)-1)</code>.
  And in view of the fact that standard is very conservative with the <code>noexcept</code> specifications in the library specification, 
  this makes <code>apply</code> lacks the <code>noexcept</code> specification and becomes a non-<code>noexcept</code> function.
  Fortunately, the standard also defines the
  semantics of <code>apply</code> in terms of another equivalent-to <em>Effects</em>, this part constitutes the effective <code>noexcept</code> specification of <code>operator*()</code>.</p>
  
  <p>But if we look at <code>apply</code> closely, according to its <em>Effects</em> in <a href="https://eel.is/c++draft/tuple.apply">[tuple.apply]</a>:</p>
  
      <pre><code>  
      template&lt;class F, class Tuple, size_t... I&gt;
      constexpr decltype(auto) <em>apply-impl</em>(F&& f, Tuple&& t, index_sequence&lt;I...&gt;) {
                                                                            // exposition only
        return <em>INVOKE</em>(std::forward&lt;F&gt;(f), get&lt;I&gt;(std::forward&lt;Tuple&gt;(t))...)
      }
      </code></pre>
  
  <p>It just simply uses <code>index_sequence</code> to expand <code>get</code> to extract the elements of <code>tuple</code>  
  and then forward them to <code><em>INVOKE</em></code> together with callable. And since <code>get</code> is a <code>noexcept</code> function 
  (except for the <code>subrange</code>-overload, but the standard does not specify whether <code>apply</code> can be applied to <code>subrange</code>) 
  and <code>invoke</code> is conditional <code>noexcept</code>, I think there is no reason not to make <code>apply</code> "transparently" become conditional <code>noexcept</code>.</p>
  
  <p>In my opinion, <code>invoke(f, args...)</code> should be completely equivalent to <code>apply(f, forward_as_tuple(args...))</code>, 
  adding <code>noexcept</code> to <code>apply</code> can easily achieve this and make it more consistent with <code>invoke</code>.</p>
  
  <a name="Impact on the standard"></a>
  <h2>Impact on the standard</h2>
  
  <p>Since it is a pure change for <code>apply</code>, there will be no impact.
  For the <code>zip</code> family, <code>apply</code> can "indeed" be used for its implementation. 
  This also allows users to freely add <code>noexcept</code> specification to the functions implemented through <code>apply</code> in the future.</p>
  
  <a name="Implementation experience"></a>
  <h2>Implementation experience</h2>
  
  This proposal has been implemented by
  <a href="https://github.com/llvm/llvm-project/blob/d2b0df35afb7184f5a68f67d6ed0c6230688df7f/libcxx/include/tuple#L1576">libstdc++</a>
  and <a href="https://github.com/gcc-mirror/gcc/blob/76c6be48b7841524974754f8ea7533b82c7de77e/libstdc%2B%2B-v3/include/std/tuple#L1858">libc++</a>, 
  and the libstdc++'s implementation is based on the premise that <code>std::get</code> never throws. 
  <a href="https://github.com/microsoft/STL/blob/205aed72533849619a6dadbef44eab541a75c549/stl/inc/tuple#L978">MSVC-STL</a> is consistent with standard and does not add <code>noexcept</code> specification to <code>apply</code>.
  
  <a name="Proposed-change"></a>
  <h2>Proposed change</h2>
  
  </div>
  <div>
  <ol>
  <ol>
  <li>
  <p>Edit 22.4.2 <a href="https://eel.is/c++draft/tuple.syn">[tuple.syn]</a> as indicated:</p>
  <pre>
// <em><a href="https://eel.is/c++draft/tuple.apply">[tuple.apply]</a>, calling a function with a tuple of arguments</em>
template&lt;class F, class Tuple&gt;
  constexpr decltype(auto) apply(F&amp;&amp; f, Tuple&amp;&amp; t) <ins>noexcept(<em>see below</em>)</ins>;
  </pre>
  </li>
  <li>
  <p>Edit 22.4.5 <a href="https://eel.is/c++draft/tuple.apply">[tuple.apply]</a> as indicated:</p>
  <pre>template&lt;class F, class Tuple&gt;
  constexpr decltype(auto) apply(F&amp;&amp; f, Tuple&amp;&amp; t) <ins>noexcept(<em>see below</em>)</ins>;
  </pre>
  -1- <em>Effects:</em> Given the exposition-only function:
  <blockquote>
  <pre>
  namespace std {
    template&lt;class F, class Tuple, size_t... I&gt;
    constexpr decltype(auto) <em>apply-impl</em>(F&amp;&amp; f, Tuple&amp;&amp; t, index_sequence&lt;I...&gt;) {     
                                                                          // <em>exposition only</em>
      return <em>INVOKE</em>(std::forward&lt;F&gt;(f), get&lt;I&gt;(std::forward&lt;Tuple&gt;(t))<wbr>...);     // <em>see <a href="https://eel.is/c++draft/func.require">[func.require]</a></em>
    }
  }
  </pre>
  Equivalent to:
     <pre>
  return <em>apply-impl</em>(std::forward&lt;F&gt;(f), std::forward&lt;Tuple&gt;(t),
                    make_index_sequence&lt;tuple_<wbr>size_v&lt;remove_reference_t&lt;<wbr>Tuple&gt;&gt;&gt;{});
  </pre>
  </blockquote>
  <ins>
  -2- <em>Remarks</em>: Let <tt>I</tt> be the pack <tt>0</tt>, <tt>1</tt>, …, <tt>(tuple_size_v&lt;remove_<wbr>reference_t&lt;Tuple&gt;&gt;-1)</tt>. The exception specification is equivalent to: <tt>noexcept(invoke(std::forward&lt;<wbr>F&gt;(f), get&lt;I&gt;(std::forward&lt;Tuple&gt;(t))<wbr>...))</tt>.
  </ins>
  </li>
  </ol>
  </ol>
  </div>
  
  <a name="References"></a>
  <h2>References</h2>
  
  <dd>
    <dt id="biblio-n4892">[N4901]
    <dd>Thomas Köppe. Working Draft, Standard for Programming Language C++. URL: <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/n4901.pdf">http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/n4901.pdf</a>
    <dt id="biblio-p2321">[P2321]
    <dd>Tim Song. <code>zip</code>. URL: <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2321r2.html">http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2321r2.html</a>
    <dt id="biblio-libc">[LIBC++]
    <dd> <code>apply</code> implementation in libc++. URL: <a href="https://github.com/llvm/llvm-project/blob/d2b0df35afb7184f5a68f67d6ed0c6230688df7f/libcxx/include/tuple#L1576">https://github.com/llvm/llvm-project/blob/d2b0df35afb7184f5a68f67d6ed0c6230688df7f/libcxx/include/tuple#L1576</a>
    <dt id="biblio-libstdc">[LIBSTDC++]
    <dd> <code>apply</code> implementation in libstdc++. URL: <a href="https://github.com/gcc-mirror/gcc/blob/76c6be48b7841524974754f8ea7533b82c7de77e/libstdc%2B%2B-v3/include/std/tuple#L1858">https://github.com/llvm/llvm-project/blob/d2b0df35afb7184f5a68f67d6ed0c6230688df7f/libcxx/include/tuple#L1576</a>
    <dt id="biblio-ms-stl">[MSVC-STL]
    <dd><code>apply</code> implementation in Microsoft STL. URL: <a href="https://github.com/microsoft/STL/blob/205aed72533849619a6dadbef44eab541a75c549/stl/inc/tuple#L978">https://github.com/microsoft/STL/blob/205aed72533849619a6dadbef44eab541a75c549/stl/inc/tuple#L978</a>
  </body></html>