<html>

<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>
    Remove value-type invocability requirement from indirect unary callable concepts
  </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>P3757R0</td>
      </tr>
      <tr>
        <td>Date</td>
        <td>2025-06-21</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>Remove value-type invocability requirement from indirect unary callable concepts
  </h1>
  <ul>

    <li>Abstract</li>
    <li>Revision history</li>
    <li>Motivation</li>
    <li>Discussion</li>
    <li>Wording</li>
    <li>References</li>

  </ul>
  <a name="Abstract"></a>
  <h2>Abstract</h2>
  <p>This is the follow up of <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2997r1.html">P2997</a>.
  </p>
  <p>
    This paper proposes removing the requirement that a callable be invocable with the value-type of an iterator in the
    <code>indirectly_unary_invocable</code> and related concepts.
    This change simplifies usage, avoids cryptic template errors, and enables support for projectors that return
    non-movable types.
  </p>
  <a name="Revision history"></a>
  <h2>Revision history</h2>
  <p>
  <h3>R0</h3>
  <p>Initial revision.</p>
  </p>
  </ol>
  </p>
  <a name="Motivation"></a>
  <h2>Motivation</h2>
  <p>The <code>indirectly_unary_invocable</code> currently has the following definition:</p>
  <pre>
  template&lt;class F, class I&gt;
    concept indirectly_unary_invocable =
      indirectly_readable&lt;I&gt; &&
      copy_constructible&lt;F&gt; &&
      invocable&lt;F&, <span style="color:red;font-weight:bold"><i>indirect-value-t</i>&lt;I&gt;</span>&gt; &&
      invocable&lt;F&, iter_reference_t&lt;I&gt;&gt; &&
      common_reference_with&lt;
        invoke_result_t&lt;F&, <span style="color:red;font-weight:bold"><i>indirect-value-t</i>&lt;I&gt;</span>&gt;,
        invoke_result_t&lt;F&, iter_reference_t&lt;I&gt;&gt;&gt;;</pre>
  <p>
    The red part is the <i>lvalue</i> reference to value-type of iterator in most cases, i.e.,
    <code>iter_value_t&lt;I&gt;&</code>:
    the concept requires <code>F</code> to be invocable with both the reference type and the value-type of
    <code>I</code>, even if the value-type is never actually used in practice.
  </p>
  <p>
    Unfortunately, this prevents some intuitive spellings, for example (<a
      href="https://godbolt.org/z/Gb6P7vzMW">godbolt</a>):</p>
  <pre>
  vector&lt;string&gt; v;
  ranges::for_each(v | views::as_rvalue, [](string<span style="color:red;font-weight:bold">&&</span> s) { /* */ });</pre>
  <p>
    <code>ranges::for_each</code> requires <code>indirectly_unary_invocable</code>, so the lambda
    also needs to be able to take with <code>string&</code>
    even though we pass a range of <code>string&&</code>, since <code>string&</code> cannot bind to
    <code>string&&</code>, the constraint is
    not satisfied.
  </p>
  <p>The error message from the compiler is even worse. From libc++ it is:</p>
  <pre>  &lt;source&gt;:8:1: error: no matching function for call to object of type 'const __for_each'
    8 | std::ranges::for_each(v | std::views::as_rvalue, [](std::string&& s) { /* */ });
      | ^~~~~~~~~~~~~~~~~~~~~
  /opt/compiler-explorer/clang-20.1.0/bin/../include/c++/v1/__algorithm/ranges_for_each.h:63:3: note: candidate template ignored: constraints not satisfied [with _Range = invoke_result_t&lt;const __fn &, vector&lt;string, allocator&lt;string&gt;&gt; &&gt;, _Proj = identity, _Func = (lambda at &lt;source&gt;:8:50)]
    63 |   operator()(_Range&& __range, _Func __func, _Proj __proj = {}) const {
        |   ^
  /opt/compiler-explorer/clang-20.1.0/bin/../include/c++/v1/__algorithm/ranges_for_each.h:61:13: note: because 'indirectly_unary_invocable&lt;(lambda at &lt;source&gt;:8:50), projected&lt;iterator_t&lt;as_rvalue_view&lt;ref_view&lt;vector&lt;string, allocator&lt;string&gt; &gt; &gt; &gt; &gt;, identity&gt; &gt;' evaluated to false
    61 |             indirectly_unary_invocable&lt;projected&lt;iterator_t&lt;_Range&gt;, _Proj&gt;&gt; _Func&gt;
        |             ^
  /opt/compiler-explorer/clang-20.1.0/bin/../include/c++/v1/__iterator/concepts.h:206:60: note: <span style="color:red;font-weight:bold">because 'invocable&lt;(lambda at &lt;source&gt;:8:50) &, __indirect_value_t&lt;__type&gt; &gt;' evaluated to false</span>
    206 |     indirectly_readable&lt;_It&gt; && copy_constructible&lt;_Fp&gt; && invocable&lt;_Fp&, __indirect_value_t&lt;_It&gt;&gt; &&
        |                                                            ^
  /opt/compiler-explorer/clang-20.1.0/bin/../include/c++/v1/__concepts/invocable.h:28:3: note: because 'std::invoke(std::forward&lt;_Fn&gt;(__fn), std::forward&lt;_Args&gt;(__args)...)' would be invalid: no matching function for call to 'invoke'
    28 |   std::invoke(std::forward&lt;_Fn&gt;(__fn), std::forward&lt;_Args&gt;(__args)...); // not required to be equality preserving
        |   ^
  /opt/compiler-explorer/clang-20.1.0/bin/../include/c++/v1/__algorithm/ranges_for_each.h:55:3: note: candidate function template not viable: requires at least 3 arguments, but 2 were provided
    55 |   operator()(_Iter __first, _Sent __last, _Func __func, _Proj __proj = {}) const {
        |   ^          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1 error generated.</pre>
  <p>This is cryptic and not easy to understand: the reason marked in red only shows that
    <code>invocable&lt;lambda&, __indirect_value_t&lt;__type&gt;&gt;</code> is false, and that's it. Unless the user
    actually look
    the implementation of <code>__indirect_value_t</code>, it is unlikely to know what going on here, which
    is annoying.
  </p>
  <p>Moreover, this requirement for value-type invocability also blocks other reasonable use cases,
    for example (<a href="https://godbolt.org/z/co45ooPqM">godbolt</a>):</p>
  <pre>
  struct NonMovable {
    int val_;
    NonMovable(int val) : val_(val) {}
    <span style="color:red;font-weight:bold">NonMovable(NonMovable&&) = delete;</span>
    auto operator&lt;=&gt;(const NonMovable&) const = default;
  };

  vector&lt;int&gt; v{1, 2, 3, 4, 5};
  ranges::sort(v, {}, [](int val) { return <span style="color:red;font-weight:bold">NonMovable</span>{val}; });
</pre>
  <p>
    We are trying to project the elements into non-movable-but-comparable objects for sorting,
    but this is currently prohibited because <code>projected</code> has the following signature:
  </p>
  <pre>
    template&lt;indirectly_readable I, <span style="color:red;font-weight:bold">indirectly_regular_unary_invocable&lt;I&gt; Proj</span>>
    using projected = /* */;</pre>
  <p>where <code>indirectly_unary_invocable</code> requires <code>common_reference_with&lt;invoke_result_t&lt;Proj&, <i>indirect-value-t</i>&lt;I&gt;&gt;,
invoke_result_t&lt;Proj&, iter_reference_t&lt;I&gt;&gt;&gt;</code>, so we need to
    satisfy <code>common_reference_with&lt;NonMovable, NonMovable&gt;</code> since the lambda returns
    <code>NonMovable</code>, which in turn needs to satisfy <code>convertible_to&lt;NonMovable, NonMovable&gt;</code>.
    But that is always false
    because
    non-movable objects cannot be converted to each other.
  </p>
  <p>
    In other words, the <code>common_reference_with</code> requirement involving
    <code><i>indirect-value-t</i>&lt;I&gt;</code> in
    <code>indirectly_unary_invocable</code> prevents the use of projectors that return non-movable types. This is a
    significant limitation.
  </p>
  </ol>
  <a name="Discussion"></a>
  <h2>Discussion</h2>
  <p>
    This raises the question: why does <code>indirectly_unary_invocable</code> require value-type invocability at all?
  </p>
  <p><a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2997r1.html">P2997</a>
    indirectly helps us trace the history of why the corresponding binary version concept, i.e.,
    <code>indirect_equivalence_relation</code>, requires this: because we do need to construct the value-type for
    comparing.
    For example, the implementation of <code>ranges::unique_copy</code>, which relies on
    <code>indirect_equivalence_relation</code>, is as follows:
  </p>
  <pre>
  template &lt;class _InputIter, class _OutputIter, class _BinaryPredicate,
            class _Tp&gt;
  _OutputIter __unique_copy(_InputIter __first, _InputIter __last,
                            _OutputIter __result,
                            _BinaryPredicate __binary_pred, _Tp*) {
    <span style="color:red;font-weight:bold">_Tp __value = *__first</span>;
    *__result = __value;
    while (++__first != __last)
      if (!__binary_pred(<span style="color:red;font-weight:bold">__value</span>, *__first)</span>) {
        __value = *__first;
        *++__result = __value;
      }
    return ++__result;
  }</pre>
  We can see that temporary construction of the value-type is unavoidable, as
  <code>indirect_equivalence_relation</code> spells it out:
  <pre>
  template&lt;class F, class I1, class I2 = I1&gt;
    concept indirect_equivalence_relation =
      indirectly_readable&lt;I1&gt; && indirectly_readable&lt;I2&gt; &&
      copy_constructible&lt;F&gt; &&
      equivalence_relation&lt;F&, <span style="color:red;font-weight:bold"><i>indirect-value-t</i>&lt;I1&gt;</span>, <span style="color:red;font-weight:bold"><i>indirect-value-t</i>&lt;I2&gt;</span>&gt; &&
      equivalence_relation&lt;F&, <span style="color:red;font-weight:bold"><i>indirect-value-t</i>&lt;I1&gt;</span>, iter_reference_t&lt;I2&gt;&gt; &&
      equivalence_relation&lt;F&, iter_reference_t&lt;I1&gt;, <span style="color:red;font-weight:bold"><i>indirect-value-t</i>&lt;I2&gt;</span>&gt; &&
      equivalence_relation&lt;F&, iter_reference_t&lt;I1&gt;, iter_reference_t&lt;I2&gt;&gt;;
</pre>
  <p>It appears that the unary version simply inherits this pattern from its binary counterpart.
    However, this is completely unnecessary if we look at algorithms with such constrain, for example,
    the most typical <code>ranges::for_each</code>:
  <pre>
  // for_each.  Apply a function to every element of a range.
  template &lt;class _InputIter, class _Function&gt;
  _Function for_each(_InputIter __first, _InputIter __last, _Function __f) {
    for ( ; __first != __last; ++__first)
      __f(<span style="color:red;font-weight:bold">*__first</span>);
    return __f;
  }
</pre>
  <p>
    we only dereference the iterator and invoke the function on the reference in each iteration, there is no value-type
    involved.
  </p>
  <p>
    However, the original example is rejected because of <code>indirectly_unary_invocable</code>, which is the only
    reason. The code compiles successfully if we switch to
    <code>std::for_each</code> instead.
  </p>
  <p>In the context of ranges, references are usually preferred over values. Constructing value-types is unnecessary
    unless required, as it can cause excessive copying.
    There seems to be no compelling reason to require a function to accept an iterator's lvalue value-type when
    accepting its reference would suffice.</p>
  <p>
    Moreover, other common algorithms, such as <code>count_if</code>, etc.,
    also
    do not produce value-type if we look at their implementation:
  <pre>
  template &lt;class _InputIter, class _Predicate&gt;
  typename iterator_traits&lt;_InputIter&gt;::difference_type
  count_if(_InputIter __first, _InputIter __last, _Predicate __pred) {
    typename iterator_traits&lt;_InputIter&gt;::difference_type __n = 0;
    for ( ; __first != __last; ++__first)
      if (__pred(<span style="color:red;font-weight:bold">*__first</span>))
        ++__n;
    return __n;
  }
</pre>
  However, they are currently subject to this unnecessary constraint because of <code>indirect_unary_predicate</code>.
  Here is the summary table:
  <p>
  <table class="bordered">
    <thead>
      <tr>
        <th>Algorithm</th>
        <th>Unary constraint concept</th>
        <th>Requires value-type invocation?</th>
        <th>Explanation</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td><code>ranges::for_each, for_each_n</code></td>
        <td><code>indirectly_unary_invocable</code></td>
        <td>&#10060;</td>
        <td>Only involve <code>invoke(f, invoke(proj, *it))</code></td>
      </tr>
      <tr>
        <td><code>ranges::all_of</code>, <code>any_of</code>, <code>none_of</code></td>
        <td><code>indirect_unary_predicate</code></td>
        <td>&#10060;</td>
        <td>Only involve <code>invoke(pred, invoke(proj, *it))</code></td>
      </tr>
      <tr>
        <td><code>ranges::find_if</code>, <code>find_if_not</code></td>
        <td><code>indirect_unary_predicate</code></td>
        <td>&#10060;</td>
        <td>Only involve <code>invoke(pred, invoke(proj, *it))</code></td>
      </tr>
      <tr>
        <td><code>ranges::find_last_if</code>, <code>find_last_if_not</code></td>
        <td><code>indirect_unary_predicate</code></td>
        <td>&#10060;</td>
        <td>Only involve <code>invoke(pred, invoke(proj, *it))</code></td>
      </tr>
      <tr>
        <td><code>ranges::count_if</code></td>
        <td><code>indirect_unary_predicate</code></td>
        <td>&#10060;</td>
        <td>Only involve <code>invoke(pred, invoke(proj, *it))</code></td>
      </tr>
      <tr>
        <td><code>ranges::copy_if</code>, <code>replace_if</code>, <code>replace_copy_if</code>, <code>remove_if</code>,
          <code>remove_copy_if</code>
        </td>
        <td><code>indirect_unary_predicate</code></td>
        <td>&#10060;</td>
        <td>Only involve <code>invoke(pred, invoke(proj, *it))</code></td>
      </tr>
      <tr>
        <td><code>ranges::is_partitioned</code></td>
        <td><code>indirect_unary_predicate</code></td>
        <td>&#10060;</td>
        <td>Only involve <code>invoke(pred, invoke(proj, *it))</code></td>
      </tr>
      <tr>
        <td><code>ranges::partition</code>, <code>stable_partition</code>, <code>partition_copy</code>,
          <code>partition_point</code>
        </td>
        <td><code>indirect_unary_predicate</code></td>
        <td>&#10060;</td>
        <td>Only involve <code>invoke(pred, invoke(proj, *it))</code></td>
      </tr>
      <tr>
        <td><code>ranges::filter_view</code>, <code>take_while_view</code>, <code>drop_while_view</code></td>
        <td><code>indirect_unary_predicate</code></td>
        <td>&#10060;</td>
        <td>Only involve <code>invoke(pred, *it)</code></td>
      </tr>
      <tr>
        <td><code>std::projected&lt;I, Proj&gt;</code></td>
        <td><code>indirectly_regular_unary_invocable</code></td>
        <td>&#10060;</td>
        <td>Only involve <code>invoke_result_t&lt;Proj&, iter_reference_t&lt;I&gt;&gt;</code></td>
      </tr>
      <tr>
        <td><code>std::projected_value_t&lt;I, Proj&gt;</code></td>
        <td><code>indirectly_regular_unary_invocable</code></td>
        <td>&#9989;</td>
        <td>Involve <code>invoke_result_t&lt;Proj&, iter_value_t&lt;I&gt;&&gt;</code></td>
      </tr>
    </tbody>
  </table>
  </p>
  <p>It is worth noting that <code>projected_value_t</code> is the only one that actually requires the use of value-type
    of iterators by desgin:</p>
  <pre>
  template&lt;indirectly_readable I, <span style="color:red;font-weight:bold">indirectly_regular_unary_invocable&lt;I&gt; Proj</span>&gt;
  using projected_value_t =
    remove_cvref_t&lt;invoke_result_t&lt;Proj&, <span style="color:red;font-weight:bold">iter_value_t&lt;I&gt;&</span>&gt;&gt;;
</pre>
  <p>This means that once we remove the value-type invocability requirement in
    <code>indirectly_regular_unary_invocable</code>,
    it is necessary to reconstrain the template parameters of <code>projected_value_t</code>:
  </p>
  <pre>
  template&lt;indirectly_readable I, <span style="color:red;font-weight:bold">copy_constructible</span> Proj&gt;
    requires <span style="color:red;font-weight:bold">invocable&lt;Proj&, iter_value_t&lt;I&gt;&&gt;</span>
  using projected_value_t =
    remove_cvref_t&lt;invoke_result_t&lt;Proj&, iter_value_t&lt;I&gt;&&gt;&gt;;
</pre>
  <p>
    And this seems to be a more appropriate signature, as the main point of
    <code>indirectly_regular_unary_invocable</code> is to check invocability on
    the reference, which is irrelevant compared to the actual definition of <code>projected_value_t</code>.
  </p>
  <p>
    In summary, the current design of indirect unary callable concepts leads to needless complexity. Relaxing them to
    only focus on the reference type of iterator would allow constraint algorithms to behave more sensibly without
    triggering cryptic errors. Furthermore, this change enables support for projectors that return non-movable objects,
    which is a significant improvement.</p>
  <a name="Wording"></a>
  <h2>Wording</h2>
  <ol>
    <li>
      <p>
        Bump <code>__cpp_lib_ranges</code> in 17.3.2 <a href="https://eel.is/c++draft/version.syn">[version.syn]</a>:
      </p>
      <blockquote>
        <pre>#define __cpp_lib_ranges                            <del>202406L</del><ins>2025XXL</ins>
  // <i>also</i> in &lt;algorithm&gt;, &lt;functional&gt;, &lt;iterator&gt;, &lt;memory&gt;, <ranges&gt;</pre>
      </blockquote>
    </li>
    <li>
      <p>Modify 24.2 <a href="https://eel.is/c++draft/iterator.synopsis">[iterator.synopsis]</a>
        as indicated:
      </p>
      <blockquote>
        <pre>
#include &lt;compare&gt;              // <i>see [compare.syn]</i>
#include &lt;concepts&gt;             // <i>see [concepts.syn]</i>
namespace std {
  [&hellip;]

  template&lt;indirectly_readable I, <del>indirectly_regular_unary_invocable&lt;I&gt;</del><ins>copy_constructible</ins> Proj&gt;
    <ins>requires invocable&lt;Proj&, iter_value_t&lt;I&gt;&&gt;</ins>
  using projected_value_t =                                                         // <i>freestanding</i>
    remove_cvref_t&lt;invoke_result_t&lt;Proj&, iter_value_t&lt;I&gt;&&gt;&gt;;

  [&hellip;]
}
        </pre>
      </blockquote>
    </li>
    <li>
      <p>Modify 25.2 <a
          href="https://eel.is/c++draft/indirectcallable.indirectinvocable">[indirectcallable.indirectinvocable]</a>
        as indicated:
      </p>
      <blockquote>
        <pre>
namespace std {
  template&lt;class F, class I&gt;
    concept indirectly_unary_invocable =
      indirectly_readable&lt;I&gt; &&
      copy_constructible&lt;F&gt; &&
      <del>invocable&lt;F&, <i>indirect-value-t</i>&lt;I&gt;&gt; &&</del>
      invocable&lt;F&, iter_reference_t&lt;I&gt;&gt; <del>&&
      common_reference_with&lt;
        invoke_result_t&lt;F&, <i>indirect-value-t</i>&lt;I&gt;&gt;,
        invoke_result_t&lt;F&, iter_reference_t&lt;I&gt;&gt;&gt;</del>;

  template&lt;class F, class I&gt;
    concept indirectly_regular_unary_invocable =
      indirectly_readable&lt;I&gt; &&
      copy_constructible&lt;F&gt; &&
      <del>regular_invocable&lt;F&, <i>indirect-value-t</i>&lt;I&gt;&gt; &&</del>
      regular_invocable&lt;F&, iter_reference_t&lt;I&gt;&gt; <del>&&
      common_reference_with&lt;
        invoke_result_t&lt;F&, <i>indirect-value-t</i>&lt;I&gt;&gt;,
        invoke_result_t&lt;F&, iter_reference_t&lt;I&gt;&gt;&gt;</del>;

  template&lt;class F, class I&gt;
    concept indirect_unary_predicate =
      indirectly_readable&lt;I&gt; &&
      copy_constructible&lt;F&gt; &&
      <del>predicate&lt;F&, <i>indirect-value-t</i>&lt;I&gt;&gt; &&</del>
      predicate&lt;F&, iter_reference_t&lt;I&gt;&gt;;

  [&hellip;]
}
        </pre>
      </blockquote>
    </li>
  </ol>
  </div>
  <a name="References"></a>
  <h2>References</h2>
  <dd>
  <dt id="biblio-p2997">[P2997R1]
  <dd>Barry Revzin. Removing the common reference requirement from the indirectly invocable concepts. URL: <a
      href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2997r1.html">https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2997r1.html</a>
</body>

</html>