Skip to content

Commit 5e2baff

Browse files
committed
New issue from Jiang An: "ranges::for_each(_n) should be less constrained"
1 parent fed5bab commit 5e2baff

File tree

1 file changed

+121
-0
lines changed

1 file changed

+121
-0
lines changed

xml/issue4241.xml

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<?xml version='1.0' encoding='utf-8' standalone='no'?>
2+
<!DOCTYPE issue SYSTEM "lwg-issue.dtd">
3+
4+
<issue num="4241" status="New">
5+
<title>`ranges::for_each(_n)` should be less constrained</title>
6+
<section>
7+
<sref ref="[alg.foreach]"/>
8+
</section>
9+
<submitter>Jiang An</submitter>
10+
<date>08 Apr 2025</date>
11+
<priority>99</priority>
12+
13+
<discussion>
14+
<p>
15+
Currently, `ranges::for_each(_n)` are constrained with `indirectly_unary_invocable`,
16+
which doesn't meet the actual use of the range elements. These algorithms are only
17+
expected to invoke the callable object with the possibly projected elements, and not
18+
to use any element as the value type. Moreover, `indirectly_unary_invocable` requires
19+
the callable object to be copy constructible, which might be undesired because the
20+
corresponding `std::for_each(_n)` only require move constructibilty.
21+
<p/>
22+
LWG <iref ref="4171"/> talked about the breakage around `ranges::for_each` introduced
23+
by <paper num="P2609R3"/>. P2609R3 looks like a reasonable fix as long as the affected
24+
algorithms potentially use the intermediate element values copied as
25+
<tt>std::iter_value_t&lt;I&gt;</tt>. However, when the algorithm is not expected to or
26+
even required not to do this, P2609R3 can bring unexpected impacts. It seems that
27+
constraints around `iter_value_t` should be avoided for such an algorithm.
28+
</p>
29+
</discussion>
30+
31+
<resolution>
32+
<p>
33+
This wording is relative to <paper num="N5008"/>.
34+
</p>
35+
36+
<ol>
37+
38+
<li><p>Modify <sref ref="[algorithm.syn]"/>, header <tt>&lt;algorithm&gt;</tt> synopsis, as indicated:</p>
39+
40+
<blockquote>
41+
<pre>
42+
[&hellip;]
43+
namespace ranges {
44+
template&lt;class I, class F&gt;
45+
using for_each_result = in_fun_result&lt;I, F&gt;;
46+
47+
template&lt;input_iterator I, sentinel_for&lt;I&gt; S, class Proj = identity,
48+
<del>indirectly_unary_invocable&lt;projected&lt;I, Proj&gt;&gt;</del><ins>move_constructible</ins> Fun&gt;
49+
<ins>requires invocable&lt;Fun&amp;, iter_reference_t&lt;projected&lt;I, Proj&gt;&gt;&gt;</ins>
50+
constexpr for_each_result&lt;I, Fun&gt;
51+
for_each(I first, S last, Fun f, Proj proj = {});
52+
template&lt;input_range R, class Proj = identity,
53+
<del>indirectly_unary_invocable&lt;projected&lt;iterator_t&lt;R&gt;, Proj&gt;&gt;</del><ins>move_constructible</ins> Fun&gt;
54+
<ins>requires invocable&lt;Fun&amp;, iter_reference_t&lt;projected&lt;iterator_t&lt;R&gt;, Proj&gt;&gt;&gt;</ins>
55+
constexpr for_each_result&lt;borrowed_iterator_t&lt;R&gt;, Fun&gt;
56+
for_each(R&amp;&amp; r, Fun f, Proj proj = {});
57+
}
58+
[&hellip;]
59+
namespace ranges {
60+
template&lt;class I, class F&gt;
61+
using for_each_n_result = in_fun_result&lt;I, F&gt;;
62+
63+
template&lt;input_iterator I, class Proj = identity,
64+
<del>indirectly_unary_invocable&lt;projected&lt;I, Proj&gt;&gt;</del><ins>move_constructible</ins> Fun&gt;
65+
<ins>requires invocable&lt;Fun&amp;, iter_reference_t&lt;projected&lt;I, Proj&gt;&gt;&gt;</ins>
66+
constexpr for_each_n_result&lt;I, Fun&gt;
67+
for_each_n(I first, iter_difference_t&lt;I&gt; n, Fun f, Proj proj = {});
68+
}
69+
[&hellip;]
70+
</pre>
71+
</blockquote>
72+
73+
</li>
74+
75+
<li><p>Modify <sref ref="[alg.foreach]"/> as indicated:</p>
76+
77+
<blockquote>
78+
<pre>
79+
template&lt;input_iterator I, sentinel_for&lt;I&gt; S, class Proj = identity,
80+
<del>indirectly_unary_invocable&lt;projected&lt;I, Proj&gt;&gt;</del><ins>move_constructible</ins> Fun&gt;
81+
<ins>requires invocable&lt;Fun&amp;, iter_reference_t&lt;projected&lt;I, Proj&gt;&gt;&gt;</ins>
82+
constexpr ranges::for_each_result&lt;I, Fun&gt;
83+
ranges::for_each(I first, S last, Fun f, Proj proj = {});
84+
template&lt;input_range R, class Proj = identity,
85+
<del>indirectly_unary_invocable&lt;projected&lt;iterator_t&lt;R&gt;, Proj&gt;&gt;</del><ins>move_constructible</ins> Fun&gt;
86+
<ins>requires invocable&lt;Fun&amp;, iter_reference_t&lt;projected&lt;iterator_t&gt;R&gt;, Proj&gt;&gt;&gt;</ins>
87+
constexpr ranges::for_each_result&lt;borrowed_iterator_t&lt;R&gt;, Fun&gt;
88+
ranges::for_each(R&amp;&amp; r, Fun f, Proj proj = {});
89+
</pre>
90+
<blockquote>
91+
<p>
92+
[&hellip;]
93+
<p/>
94+
<del>-15- [<i>Note 6</i>: The overloads in namespace `ranges` require `Fun` to model `copy_constructible`. &mdash; <i>end note</i>]</del>
95+
</p>
96+
</blockquote>
97+
<p>
98+
[&hellip;]
99+
</p>
100+
<pre>
101+
template&lt;input_iterator I, class Proj = identity,
102+
<del>indirectly_unary_invocable&lt;projected&lt;I, Proj&gt;&gt;</del><ins>move_constructible</ins> Fun&gt;
103+
<ins>requires invocable&lt;Fun&amp;, iter_reference_t&lt;projected&lt;I, Proj&gt;&gt;&gt;</ins>
104+
constexpr ranges::for_each_n_result&lt;I, Fun&gt;
105+
ranges::for_each_n(I first, iter_difference_t&lt;I&gt; n, Fun f, Proj proj = {});
106+
</pre>
107+
<blockquote>
108+
<p>
109+
[&hellip;]
110+
<p/>
111+
<del>-30- [<i>Note 11</i>: The overload in namespace `ranges` requires `Fun` to model `copy_constructible`. &mdash; <i>end note</i>]</del>
112+
</p>
113+
</blockquote>
114+
</blockquote>
115+
116+
</li>
117+
118+
</ol>
119+
</resolution>
120+
121+
</issue>

0 commit comments

Comments
 (0)