Skip to content

Commit 589637e

Browse files
committed
Implementation experience comments from Tomasz on LWG 4264
1 parent 8db2e17 commit 589637e

File tree

1 file changed

+166
-5
lines changed

1 file changed

+166
-5
lines changed

xml/issue4264.xml

Lines changed: 166 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ Currently the wording in <sref ref="[func.wrap.general]"/> allows implementation
1414
to avoid double indirection when constructing owning functions wrappers from another one:
1515
</p>
1616
<blockquote>
17-
<p>
17+
<p>
1818
-2- Let <tt>t</tt> be an object of a type that is a specialization of <tt>function</tt>,
1919
<tt>copyable_function</tt>, or <tt>move_only_function</tt>, such that the target object
20-
<tt>x</tt> of <tt>t</tt> has a type that is a specialization of <tt>function</tt>,
20+
<tt>x</tt> of <tt>t</tt> has a type that is a specialization of <tt>function</tt>,
2121
<tt>copyable_function</tt>, or <tt>move_only_function</tt>.
2222
Each argument of the invocation of <tt>x</tt> evaluated as part of the invocation of <tt>t</tt>
2323
may alias an argument in the same position in the invocation of <tt>t</tt> that has the same type,
@@ -38,6 +38,131 @@ an implementation to perform such an optimization. As a consequence, it is accep
3838
to specify the allowance for all combinations of polymorphic wrappers, even for creating an
3939
owning wrapper from a non-owning one, where implementing such an optimization may not be possible.
4040
</p>
41+
42+
<superseded>
43+
<p>
44+
This wording is relative to <paper num="N5008"/>.
45+
</p>
46+
<ol>
47+
48+
<li><p>Modify <sref ref="[func.wrap.general]"/> as indicated:</p>
49+
50+
<blockquote>
51+
<p>
52+
-2- Let <tt>t</tt> be an object of a type that is a specialization of <tt>function</tt>,
53+
<tt>copyable_function</tt>, <del>or</del> <tt>move_only_function</tt><ins>, or <tt>function_ref</tt></ins>,
54+
such that the target object <tt>x</tt> of <tt>t</tt> has a type that is a specialization of <tt>function</tt>,
55+
<tt>copyable_function</tt>, <del>or</del><tt>move_only_function</tt><ins>, or <tt>function_ref</tt></ins>.
56+
Each argument of the invocation of <tt>x</tt> evaluated as part of the invocation of <tt>t</tt>
57+
may alias an argument in the same position in the invocation of <tt>t</tt> that has the same type,
58+
even if the corresponding parameter is not of reference type.
59+
</p>
60+
</blockquote>
61+
62+
</li>
63+
</ol>
64+
65+
</superseded>
66+
67+
<note>2024-05-21; Tomasz's comment and upates proposed resolution</note>
68+
69+
<p>
70+
After implementing double indirection avoidance in the libstdc++, I have realized
71+
that above wording change is insufficient to cover all user observable effects of
72+
the change. Revelant quote from the <a href="https://gcc.gnu.org/pipermail/libstdc++/2025-May/061561.html">
73+
Avoid double indirection in function_ref</a> from libstdc++ mailing lists:
74+
</p>
75+
76+
<blockquote>
77+
<p>
78+
To avoidance of double indirection requires that constructed <tt>function_ref</tt>,
79+
refers directly to the target function of the source, instead of source,
80+
and this is visible after the assigment:
81+
</p>
82+
83+
<pre>
84+
void foo() noexcept;
85+
void bar() noexcept;
86+
87+
std::function_ref&lt;void() noexcept&gt; sr(&amp;foo);
88+
std::function_ref&lt;void()&gt; dr(sr);
89+
dr(); // calls `foo` regardless of implementation
90+
91+
sr = &amp;bar;
92+
sr(); // calls `bar`
93+
dr(); // still calls `foo` if we avoid indirection,
94+
// calls `bar` if we do not
95+
</pre>
96+
97+
<p>
98+
Similary for <tt>move_only_function</tt>/<tt>copyable_function</tt> source:
99+
</p>
100+
101+
<pre>
102+
std::move_only_function&lt;void()&gt; sm;
103+
std::function_ref&lt;void()&gt; dm(sm);
104+
105+
dm(); // UB because `sm` is empty
106+
107+
sm = &amp;foo;
108+
109+
dm(); // remains UB if we avoid indirection,
110+
// calls `bar` if we do not.
111+
</pre>
112+
113+
<p>
114+
While we may want to allow skipping indirection for function_ref,
115+
as this produces same behavior as in case for copy constructor (matching
116+
signatures):
117+
</p>
118+
119+
<pre>
120+
void foo() noexcept;
121+
void bar() noexcept;
122+
123+
std::function_ref&lt;void() noexcept&gt; sr(&amp;foo);
124+
std::function_ref&lt;void() noexcept&gt; dr(sr); // copy-cosntructor
125+
dr(); // calls `foo` regardless of implementation
126+
127+
sr = &amp;bar;
128+
sr(); // calls `bar`
129+
dr(); // still calls `foo` if we avoid indirection
130+
</pre>
131+
132+
133+
<p>
134+
I do not think this is acceptable for <tt>move_only_function</tt>.
135+
&hellip;
136+
</p>
137+
138+
<p>
139+
Note that for the same reason, implementations are not free to avoid
140+
dangling when constructing <tt>function_ref</tt> from <tt>reference_wrapper</tt>:
141+
</p>
142+
143+
<pre>
144+
auto srw = std::ref(&amp;foo);
145+
std::function_ref&lt;void()&gt; drw(srw);
146+
drw(); // calls `foo`
147+
148+
srw = std::ref(&amp;bar);
149+
drw(); // calls `foo` if we unwrap referenc wrapper,
150+
// calls `bar` otherwise.
151+
</pre>
152+
153+
<p>
154+
Note that this is limited to <tt>function_ref</tt> due reference nature of this
155+
wrapper.
156+
</p>
157+
158+
</blockquote>
159+
160+
<p>
161+
The updated resolution allows indirection but making it unspecified if
162+
<tt>function_ref</tt> constructed from other <tt>function_ref</tt> specialization,
163+
will refer to source object or its target.
164+
</p>
165+
41166
</discussion>
42167

43168
<resolution>
@@ -49,20 +174,56 @@ This wording is relative to <paper num="N5008"/>.
49174
<li><p>Modify <sref ref="[func.wrap.general]"/> as indicated:</p>
50175

51176
<blockquote>
52-
<p>
177+
<p>
53178
-2- Let <tt>t</tt> be an object of a type that is a specialization of <tt>function</tt>,
54179
<tt>copyable_function</tt>, <del>or</del> <tt>move_only_function</tt><ins>, or <tt>function_ref</tt></ins>,
55-
such that the target object <tt>x</tt> of <tt>t</tt> has a type that is a specialization of <tt>function</tt>,
180+
such that the target object <tt>x</tt> of <tt>t</tt> has a type that is a specialization of <tt>function</tt>,
56181
<tt>copyable_function</tt>, <del>or</del><tt>move_only_function</tt><ins>, or <tt>function_ref</tt></ins>.
57182
Each argument of the invocation of <tt>x</tt> evaluated as part of the invocation of <tt>t</tt>
58183
may alias an argument in the same position in the invocation of <tt>t</tt> that has the same type,
59184
even if the corresponding parameter is not of reference type.
60185
</p>
61186
</blockquote>
62187

188+
</li>
189+
190+
<li><p>Modify <sref ref="[func.wrap.ref.ctor]"/> as indicated:</p>
191+
192+
<blockquote>
193+
<pre>
194+
template&lt;class F&gt; constexpr function_ref(F&amp;&amp;) noexcept;
195+
</pre>
196+
<blockquote>
197+
[&hellip;]
198+
<p>-7- <i>Effects</i>:
199+
Initializes <tt><i>bound-entity</i></tt> with <tt>addressof(f)</tt>
200+
and <tt><i>thunk-ptr</i></tt> with the address of a function <tt><i>thunk</i></tt> such that
201+
<tt><i>thunk</i>(<i>bound-entity</i>, <i>call-args</i>...)</tt> is expression-equivalent
202+
(<sref ref="[defns.expression.equivalent]"/>) to
203+
<tt>invoke_r&lt;R&gt;(static_cast&lt;cv T&amp;&gt;(f), <i>call-args</i>...)</tt>.
204+
</p>
205+
<ins><p>-X- <i>Remarks</i>:
206+
If <tt>remove_cveref_t&lt;F&gt;</tt> is specialization of <tt>function_ref</tt> implementation
207+
may initialize <tt><i>bound-entity</i></tt> with <tt><i>bound-entity</i></tt> of <tt>f</tt>.
208+
[<i>Example:</i>:</p>
209+
<pre>
210+
void f1() noexcept;
211+
void f2() noexcept;
212+
213+
function_ref&lt;void() noexcept&gt; r1(&amp;r1);
214+
function_ref&lt;void()&gt; r2(r1);
215+
r1 = &amp;f2;
216+
f2(); // it is unspecified if `f1` or `f2` is invoked
217+
</pre>
218+
<p> &mdash; <i>end example</i>]</p>
219+
</ins>
220+
</blockquote>
221+
</blockquote>
222+
63223
</li>
64224
</ol>
65225

226+
66227
</resolution>
67228

68-
</issue>
229+
</issue>

0 commit comments

Comments
 (0)