<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Issue 4095: ranges::fold_meow should explicitly spell out the return type</title>
<meta property="og:title" content="Issue 4095: ranges::fold_meow should explicitly spell out the return type">
<meta property="og:description" content="C++ library issue. Status: Tentatively NAD">
<meta property="og:url" content="https://cplusplus.github.io/LWG/issue4095.html">
<meta property="og:type" content="website">
<meta property="og:image" content="http://cplusplus.github.io/LWG/images/cpp_logo.png">
<meta property="og:image:alt" content="C++ logo">
<style>
  p {text-align:justify}
  li {text-align:justify}
  pre code.backtick::before { content: "`" }
  pre code.backtick::after { content: "`" }
  blockquote.note
  {
    background-color:#E0E0E0;
    padding-left: 15px;
    padding-right: 15px;
    padding-top: 1px;
    padding-bottom: 1px;
  }
  ins {background-color:#A0FFA0}
  del {background-color:#FFA0A0}
  table.issues-index { border: 1px solid; border-collapse: collapse; }
  table.issues-index th { text-align: center; padding: 4px; border: 1px solid; }
  table.issues-index td { padding: 4px; border: 1px solid; }
  table.issues-index td:nth-child(1) { text-align: right; }
  table.issues-index td:nth-child(2) { text-align: left; }
  table.issues-index td:nth-child(3) { text-align: left; }
  table.issues-index td:nth-child(4) { text-align: left; }
  table.issues-index td:nth-child(5) { text-align: center; }
  table.issues-index td:nth-child(6) { text-align: center; }
  table.issues-index td:nth-child(7) { text-align: left; }
  table.issues-index td:nth-child(5) span.no-pr { color: red; }
  @media (prefers-color-scheme: dark) {
     html {
        color: #ddd;
        background-color: black;
     }
     ins {
        background-color: #225522
     }
     del {
        background-color: #662222
     }
     a {
        color: #6af
     }
     a:visited {
        color: #6af
     }
     blockquote.note
     {
        background-color: rgba(255, 255, 255, .10)
     }
  }
</style>
</head>
<body>
<hr>
<p><em>This page is a snapshot from the LWG issues list, see the <a href="lwg-active.html">Library Active Issues List</a> for more information and the meaning of <a href="lwg-active.html#NAD">Tentatively NAD</a> status.</em></p>
<h3 id="4095"><a href="lwg-active.html#4095">4095</a>. <code>ranges::fold_<i>meow</i></code> should explicitly spell out the return type</h3>
<p><b>Section:</b> 26.4 <a href="https://wg21.link/algorithm.syn">[algorithm.syn]</a>, 26.6.18 <a href="https://wg21.link/alg.fold">[alg.fold]</a> <b>Status:</b> <a href="lwg-active.html#NAD">Tentatively NAD</a>
 <b>Submitter:</b> Hewill Kang <b>Opened:</b> 2024-05-03 <b>Last modified:</b> 2024-06-24</p>
<p><b>Priority: </b>Not Prioritized
</p>
<p><b>View other</b> <a href="lwg-index-open.html#algorithm.syn">active issues</a> in [algorithm.syn].</p>
<p><b>View all other</b> <a href="lwg-index.html#algorithm.syn">issues</a> in [algorithm.syn].</p>
<p><b>View all issues with</b> <a href="lwg-status.html#Tentatively NAD">Tentatively NAD</a> status.</p>
<p><b>Discussion:</b></p>
<p>
Unlike other algorithms, the return types of <code>ranges::fold_<i>meow</i></code> are specified in terms of
<code>auto</code> and <code><i>see below</i></code>, and its implementation details depend on the return types of 
other overloads through <code>decltype(fold_<i>meow</i>(...))</code>.
<p/>
This makes determining the return type of a certain overload (such as <code>fold_right_last</code>) 
extremely difficult even for experts, requiring several trips back and forth to different overloads 
to finally understand what the actual return type is. The situation is even worse for newbies because 
such a form of specifying the return type makes it impossible for the IDE to deduce the real return type, 
which is extremely user-unfriendly.
<p/>
I think that explicitly specifying the return type for these overloads not only greatly improves 
readability but also offloads the compiler from deducing the return type, which can definitely be 
considered an improvement.
<p/>
The proposed resolution does not touch the <i>Effects</i> clause and only changes the function signature 
to seek minimal changes.
</p>

<p><i>[2024-06-24; Reflector poll: NAD]</i></p>

<p>Implementations are free to spell this out if desired.</p>



<p id="res-4095"><b>Proposed resolution:</b></p>
<p>
This wording is relative to <a href="https://wg21.link/N4981" title=" Working Draft, Programming Languages — C++">N4981</a>.
</p>

<ol>

<li><p>Modify 26.4 <a href="https://wg21.link/algorithm.syn">[algorithm.syn]</a>, header <code>&lt;algorithm&gt;</code> synopsis, as indicated:</p>

<blockquote>
<pre>
#include &lt;initializer_list&gt;     // <i>see 17.11.2 <a href="https://wg21.link/initializer.list.syn">[initializer.list.syn]</a></i>

namespace std {
  [&hellip;]
  namespace ranges {
    [&hellip;]
    template&lt;input_iterator I, sentinel_for&lt;I&gt; S, class T = iter_value_t&lt;I&gt;,
             <i>indirectly-binary-left-foldable</i>&lt;T, I&gt; F&gt;
      constexpr auto fold_left(I first, S last, T init, F f) <ins>-&gt;
        decay_t&lt;invoke_result_t&lt;F&amp;, T, iter_reference_t&lt;I&gt;&gt;&gt;</ins>;

    template&lt;input_range R, class T = range_value_t&lt;R&gt;,
             <i>indirectly-binary-left-foldable</i>&lt;T, iterator_t&lt;R&gt;&gt; F&gt;
      constexpr auto fold_left(R&amp;&amp; r, T init, F f) <ins>-&gt;
        decay_t&lt;invoke_result_t&lt;F&amp;, T, range_reference_t&lt;R&gt;&gt;&gt;</ins>;

    template&lt;input_iterator I, sentinel_for&lt;I&gt; S,
             <i>indirectly-binary-left-foldable</i>&lt;iter_value_t&lt;I&gt;, I&gt; F&gt;
      requires constructible_from&lt;iter_value_t&lt;I&gt;, iter_reference_t&lt;I&gt;&gt;
      constexpr auto fold_left_first(I first, S last, F f) <ins>-&gt;
        optional&lt;decay_t&lt;invoke_result_t&lt;F&amp;, iter_value_t&lt;I&gt;, iter_reference_t&lt;I&gt;&gt;&gt;&gt;</ins>;

    template&lt;input_range R, <i>indirectly-binary-left-foldable</i>&lt;range_value_t&lt;R&gt;, iterator_t&lt;R&gt;&gt; F&gt;
      requires constructible_from&lt;range_value_t&lt;R&gt;, range_reference_t&lt;R&gt;&gt;
      constexpr auto fold_left_first(R&amp;&amp; r, F f) <ins>-&gt;
        optional&lt;decay_t&lt;invoke_result_t&lt;F&amp;, range_value_t&lt;R&gt;, range_reference_t&lt;R&gt;&gt;&gt;&gt;</ins>;

    template&lt;bidirectional_iterator I, sentinel_for&lt;I&gt; S, class T = iter_value_t&lt;I&gt;,
             <i>indirectly-binary-right-foldable</i>&lt;T, I&gt; F&gt;
      constexpr auto fold_right(I first, S last, T init, F f) <ins>-&gt;
        decay_t&lt;invoke_result_t&lt;F&amp;, iter_reference_t&lt;I&gt;, T&gt;&gt;</ins>;

    template&lt;bidirectional_range R, class T = range_value_t&lt;R&gt;,
             <i>indirectly-binary-right-foldable</i>&lt;T, iterator_t&lt;R&gt;&gt; F&gt;
      constexpr auto fold_right(R&amp;&amp; r, T init, F f) <ins>-&gt;
        decay_t&lt;invoke_result_t&lt;F&amp;, range_reference_t&lt;R&gt;, T&gt;&gt;</ins>;

    template&lt;bidirectional_iterator I, sentinel_for&lt;I&gt; S,
             <i>indirectly-binary-right-foldable</i>&lt;iter_value_t&lt;I&gt;, I&gt; F&gt;
      requires constructible_from&lt;iter_value_t&lt;I&gt;, iter_reference_t&lt;I&gt;&gt;
    constexpr auto fold_right_last(I first, S last, F f) <ins>-&gt;
      optional&lt;decay_t&lt;invoke_result_t&lt;F&amp;, iter_reference_t&lt;I&gt;, iter_value_t&lt;I&gt;&gt;&gt;&gt;</ins>;

    template&lt;bidirectional_range R,
             <i>indirectly-binary-right-foldable</i>&lt;range_value_t&lt;R&gt;, iterator_t&lt;R&gt;&gt; F&gt;
      requires constructible_from&lt;range_value_t&lt;R&gt;, range_reference_t&lt;R&gt;&gt;
    constexpr auto fold_right_last(R&amp;&amp; r, F f) <ins>-&gt;
      optional&lt;decay_t&lt;invoke_result_t&lt;F&amp;, range_reference_t&lt;R&gt;, range_value_t&lt;R&gt;&gt;&gt;&gt;</ins>;

    template&lt;class I, class T&gt;
      using fold_left_with_iter_result = in_value_result&lt;I, T&gt;;
    template&lt;class I, class T&gt;
      using fold_left_first_with_iter_result = in_value_result&lt;I, T&gt;;

    template&lt;input_iterator I, sentinel_for&lt;I&gt; S, class T = iter_value_t&lt;I&gt;,
             <i>indirectly-binary-left-foldable</i>&lt;T, I&gt; F&gt;
      constexpr <del><i>see below</i></del><ins>auto</ins> fold_left_with_iter(I first, S last, T init, F f) <ins>-&gt;
        fold_left_with_iter_result&lt;I, decay_t&lt;invoke_result_t&lt;F&amp;, T, iter_reference_t&lt;I&gt;&gt;&gt;&gt;</ins>;

    template&lt;input_range R, class T = range_value_t&lt;R&gt;,
             <i>indirectly-binary-left-foldable</i>&lt;T, iterator_t&lt;R&gt;&gt; F&gt;
      constexpr <del><i>see below</i></del><ins>auto</ins> fold_left_with_iter(R&amp;&amp; r, T init, F f) <ins>-&gt;
        fold_left_with_iter_result&lt;borrowed_iterator_t&lt;R&gt;,
                                   decay_t&lt;invoke_result_t&lt;F&amp;, T, range_reference_t&lt;R&gt;&gt;&gt;&gt;</ins>;

    template&lt;input_iterator I, sentinel_for&lt;I&gt; S,
             <i>indirectly-binary-left-foldable</i>&lt;iter_value_t&lt;I&gt;, I&gt; F&gt;
      requires constructible_from&lt;iter_value_t&lt;I&gt;, iter_reference_t&lt;I&gt;&gt;
      constexpr <del><i>see below</i></del><ins>auto</ins> fold_left_first_with_iter(I first, S last, F f) <ins>-&gt;
        fold_left_first_with_iter_result&lt;
          I, optional&lt;decay_t&lt;invoke_result_t&lt;F&amp;, iter_value_t&lt;I&gt;, iter_reference_t&lt;I&gt;&gt;&gt;&gt;&gt;</ins>;

    template&lt;input_range R,
             <i>indirectly-binary-left-foldable</i>&lt;range_value_t&lt;R&gt;, iterator_t&lt;R&gt;&gt; F&gt;
      requires constructible_from&lt;range_value_t&lt;R&gt;, range_reference_t&lt;R&gt;&gt;
      constexpr <del><i>see below</i></del><ins>auto</ins> fold_left_first_with_iter(R&amp;&amp; r, F f) <ins>-&gt;
        fold_left_first_with_iter_result&lt;
          borrowed_iterator_t&lt;R&gt;,
          optional&lt;decay_t&lt;invoke_result_t&lt;F&amp;, range_value_t&lt;R&gt;, range_reference_t&lt;R&gt;&gt;&gt;&gt;&gt;</ins>;
  }
  [&hellip;]
}
</pre>
</blockquote>

</li>

<li><p>Modify 26.6.18 <a href="https://wg21.link/alg.fold">[alg.fold]</a> as indicated:</p>

<blockquote>
<pre>
template&lt;input_iterator I, sentinel_for&lt;I&gt; S, class T = iter_value_t&lt;I&gt;,
         <i>indirectly-binary-left-foldable</i>&lt;T, I&gt; F&gt;
constexpr auto ranges::fold_left(I first, S last, T init, F f) <ins>-&gt;
  decay_t&lt;invoke_result_t&lt;F&amp;, T, iter_reference_t&lt;I&gt;&gt;&gt;</ins>;

template&lt;input_range R, class T = range_value_t&lt;R&gt;,
         <i>indirectly-binary-left-foldable</i>&lt;T, iterator_t&lt;R&gt;&gt; F&gt;
constexpr auto ranges::fold_left(R&amp;&amp; r, T init, F f) <ins>-&gt;
  decay_t&lt;invoke_result_t&lt;F&amp;, T, range_reference_t&lt;R&gt;&gt;&gt;</ins>;
</pre>
<blockquote>
<p>
-1- <i>Returns</i>:
</p>
<blockquote><pre>
ranges::fold_left_with_iter(std::move(first), last, std::move(init), f).value
</pre></blockquote>
</blockquote>
<pre>
template&lt;input_iterator I, sentinel_for&lt;I&gt; S,
         <i>indirectly-binary-left-foldable</i>&lt;iter_value_t&lt;I&gt;, I&gt; F&gt;
  requires constructible_from&lt;iter_value_t&lt;I&gt;, iter_reference_t&lt;I&gt;&gt;
  constexpr auto ranges::fold_left_first(I first, S last, F f) <ins>-&gt;
    optional&lt;decay_t&lt;invoke_result_t&lt;F&amp;, iter_value_t&lt;I&gt;, iter_reference_t&lt;I&gt;&gt;&gt;&gt;</ins>;

template&lt;input_range R, <i>indirectly-binary-left-foldable</i>&lt;range_value_t&lt;R&gt;, iterator_t&lt;R&gt;&gt; F&gt;
  requires constructible_from&lt;range_value_t&lt;R&gt;, range_reference_t&lt;R&gt;&gt;
  constexpr auto ranges::fold_left_first(R&amp;&amp; r, F f) <ins>-&gt;
    optional&lt;decay_t&lt;invoke_result_t&lt;F&amp;, range_value_t&lt;R&gt;, range_reference_t&lt;R&gt;&gt;&gt;&gt;</ins>;
</pre>
<blockquote>
<p>
-2- <i>Returns</i>:
</p>
<blockquote><pre>
ranges::fold_left_first_with_iter(std::move(first), last, f).value
</pre></blockquote>
</blockquote>
<pre>
template&lt;bidirectional_iterator I, sentinel_for&lt;I&gt; S, class T = iter_value_t&lt;I&gt;,
         <i>indirectly-binary-right-foldable</i>&lt;T, I&gt; F&gt;
  constexpr auto ranges::fold_right(I first, S last, T init, F f) <ins>-&gt;
    decay_t&lt;invoke_result_t&lt;F&amp;, iter_reference_t&lt;I&gt;, T&gt;&gt;</ins>;

template&lt;bidirectional_range R, class T = range_value_t&lt;R&gt;,
        <i>indirectly-binary-right-foldable</i>&lt;T, iterator_t&lt;R&gt;&gt; F&gt;
  constexpr auto ranges::fold_right(R&amp;&amp; r, T init, F f) <ins>-&gt;
    decay_t&lt;invoke_result_t&lt;F&amp;, range_reference_t&lt;R&gt;, T&gt;&gt;</ins>;  
</pre>
<blockquote>
<p>
-3- <i>Effects</i>: Equivalent to:
</p>
<blockquote><pre>
using U = decay_t&lt;invoke_result_t&lt;F&amp;, iter_reference_t&lt;I&gt;, T&gt;&gt;;
if (first == last)
  return U(std::move(init));
I tail = ranges::next(first, last);
U accum = invoke(f, *--tail, std::move(init));
while (first != tail)
  accum = invoke(f, *--tail, std::move(accum));
return accum;
</pre></blockquote>
</blockquote>
<pre>
template&lt;bidirectional_iterator I, sentinel_for&lt;I&gt; S,
        <i>indirectly-binary-right-foldable</i>&lt;iter_value_t&lt;I&gt;, I&gt; F&gt;
  requires constructible_from&lt;iter_value_t&lt;I&gt;, iter_reference_t&lt;I&gt;&gt;
constexpr auto ranges::fold_right_last(I first, S last, F f) <ins>-&gt;
  optional&lt;decay_t&lt;invoke_result_t&lt;F&amp;, iter_reference_t&lt;I&gt;, iter_value_t&lt;I&gt;&gt;&gt;&gt;</ins>;

template&lt;bidirectional_range R,
         <i>indirectly-binary-right-foldable</i>&lt;range_value_t&lt;R&gt;, iterator_t&lt;R&gt;&gt; F&gt;
 requires constructible_from&lt;range_value_t&lt;R&gt;, range_reference_t&lt;R&gt;&gt;
constexpr auto ranges::fold_right_last(R&amp;&amp; r, F f) <ins>-&gt;
  optional&lt;decay_t&lt;invoke_result_t&lt;F&amp;, range_reference_t&lt;R&gt;, range_value_t&lt;R&gt;&gt;&gt;&gt;</ins>;
</pre>
<blockquote>
<p>
-4- Let <code>U</code> be <code>decltype(ranges::fold_right(first, last, iter_value_t&lt;I&gt;(*first), f))</code>.
<p/>
-5- <i>Effects</i>: Equivalent to:
</p>
<blockquote><pre>
if (first == last)
  return optional&lt;U&gt;();
I tail = ranges::prev(ranges::next(first, std::move(last)));
return optional&lt;U&gt;(in_place,
  ranges::fold_right(std::move(first), tail, iter_value_t&lt;I&gt;(*tail), std::move(f)));
</pre></blockquote>
</blockquote>
<pre>
template&lt;input_iterator I, sentinel_for&lt;I&gt; S, class T = iter_value_t&lt;I&gt;,
         <i>indirectly-binary-left-foldable</i>&lt;T, I&gt; F&gt;
  constexpr <del><i>see below</i></del><ins>auto</ins> ranges::fold_left_with_iter(I first, S last, T init, F f) <ins>-&gt;
    fold_left_with_iter_result&lt;I, decay_t&lt;invoke_result_t&lt;F&amp;, T, iter_reference_t&lt;I&gt;&gt;&gt;&gt;</ins>;

template&lt;input_range R, class T = range_value_t&lt;R&gt;,
         <i>indirectly-binary-left-foldable</i>&lt;T, iterator_t&lt;R&gt;&gt; F&gt;
  constexpr <del><i>see below</i></del><ins>auto</ins> ranges::fold_left_with_iter(R&amp;&amp; r, T init, F f) <ins>-&gt;
    fold_left_with_iter_result&lt;borrowed_iterator_t&lt;R&gt;,
                               decay_t&lt;invoke_result_t&lt;F&amp;, T, range_reference_t&lt;R&gt;&gt;&gt;&gt;</ins>;
</pre>
<blockquote>
<p>
-6- Let <code>U</code> be <code>decay_t&lt;invoke_result_t&lt;F&amp;, T, iter_reference_t&lt;I&gt;&gt;&gt;</code>.
<p/>
-7- <i>Effects</i>: Equivalent to:
</p>
<blockquote><pre>
if (first == last)
  return {std::move(first), U(std::move(init))};
U accum = invoke(f, std::move(init), *first);
for (++first; first != last; ++first)
  accum = invoke(f, std::move(accum), *first);
return {std::move(first), std::move(accum)};
</pre></blockquote>
<p>
<del>-8- <i>Remarks</i>: The return type is <code>fold_left_with_iter_result&lt;I, U&gt;</code>
for the first overload and <code>fold_left_with_iter_result&lt;borrowed_iterator_t&lt;R&gt;, U&gt;</code> 
for the second overload.</del>
</p>
</blockquote>
<pre>
template&lt;input_iterator I, sentinel_for&lt;I&gt; S,
         <i>indirectly-binary-left-foldable</i>&lt;iter_value_t&lt;I&gt;, I&gt; F&gt;
  requires constructible_from&lt;iter_value_t&lt;I&gt;, iter_reference_t&lt;I&gt;&gt;
  constexpr <del><i>see below</i></del><ins>auto</ins> ranges::fold_left_first_with_iter(I first, S last, F f) <ins>-&gt;
    fold_left_first_with_iter_result&lt;
      I, optional&lt;decay_t&lt;invoke_result_t&lt;F&amp;, iter_value_t&lt;I&gt;, iter_reference_t&lt;I&gt;&gt;&gt;&gt;&gt;</ins>;

template&lt;input_range R,
         <i>indirectly-binary-left-foldable</i>&lt;range_value_t&lt;R&gt;, iterator_t&lt;R&gt;&gt; F&gt;
  requires constructible_from&lt;range_value_t&lt;R&gt;, range_reference_t&lt;R&gt;&gt;
  constexpr <del><i>see below</i></del><ins>auto</ins> ranges::fold_left_first_with_iter(R&amp;&amp; r, F f) <ins>-&gt;
    fold_left_first_with_iter_result&lt;
      borrowed_iterator_t&lt;R&gt;,
      optional&lt;decay_t&lt;invoke_result_t&lt;F&amp;, range_value_t&lt;R&gt;, range_reference_t&lt;R&gt;&gt;&gt;&gt;&gt;</ins>;
</pre>
<blockquote>
<p>
-9- Let <code>U</code> be
</p>
<blockquote><pre>
decltype(ranges::fold_left(std::move(first), last, iter_value_t&lt;I&gt;(*first), f))
</pre></blockquote>
<p>
-10- <i>Effects</i>: Equivalent to:
</p>
<blockquote><pre>
if (first == last)
  return {std::move(first), optional&lt;U&gt;()};
optional&lt;U&gt; init(in_place, *first);
for (++first; first != last; ++first)
  *init = invoke(f, std::move(*init), *first);
return {std::move(first), std::move(init)};
</pre></blockquote>
<p>
<del>-11- <i>Remarks</i>: The return type is <code>fold_left_first_with_iter_result&lt;I, optional&lt;U&gt;&gt;</code>
for the first overload and <code>fold_left_first_with_iter_result&lt;borrowed_iterator_t&lt;R&gt;,
  optional&lt;U&gt;&gt;</code> for the second overload.</del>
</p>
</blockquote>
</blockquote>

</li>
</ol>






</body>
</html>
