|
| 1 | +<?xml version='1.0' encoding='utf-8' standalone='no'?> |
| 2 | +<!DOCTYPE issue SYSTEM "lwg-issue.dtd"> |
| 3 | + |
| 4 | +<issue num="4319" status="New"> |
| 5 | +<title>Supporting copy-elision in function wrappers</title> |
| 6 | +<section> |
| 7 | +<sref ref="[func.require]"/> |
| 8 | +</section> |
| 9 | +<submitter>Tomasz Kamiński</submitter> |
| 10 | +<date>19 Aug 2025</date> |
| 11 | +<priority>99</priority> |
| 12 | + |
| 13 | +<discussion> |
| 14 | +<p> |
| 15 | +The wording for argument forwarding call wrappers in <sref ref="[func.require]"/> p3, |
| 16 | +</p> |
| 17 | +<blockquote><p> |
| 18 | +This forwarding step delivers rvalue arguments as rvalue references and lvalue arguments as lvalue references. |
| 19 | +</p></blockquote> |
| 20 | +<p> |
| 21 | +requires that each wrapper binds a temporary to rvalue reference |
| 22 | +(materializing it), and then pass that xvalue. This essentially |
| 23 | +codifies an implementation where wrappers provide an `operator()` that |
| 24 | +accepts <tt>Args&&...</tt>. This is fine for most of the wrappers. |
| 25 | +<p/> |
| 26 | +For some wrappers more efficient implementation strategies are possible: |
| 27 | +</p> |
| 28 | +<ul> |
| 29 | +<li><p>`bind_front(f)/bind_back(f)` without bound args could return a copy of `f`</p></li> |
| 30 | +<li><p><tt>bind_front<f>()/bind_back<f></tt> could produce a |
| 31 | +<tt>__function_wrapper<f></tt>, that for function pointers can be invoked using |
| 32 | +a surrogate function call.</p></li> |
| 33 | +</ul> |
| 34 | +<p> |
| 35 | +However, such implementation strategies are currently disallowed per |
| 36 | +<sref ref="[func.require]"/> p3, as invoking the function wrapper with |
| 37 | +a prvalue `bind_front(f)(T())` requires a temporary to be materialized, |
| 38 | +and then moved into the parameter of `f`. For example: |
| 39 | +</p> |
| 40 | +<blockquote><pre> |
| 41 | +struct M |
| 42 | +{ |
| 43 | + M() { std::cout << "Default" < std::endl; } |
| 44 | + M(M&& m) { std::cout << "Move" < std::endl; } |
| 45 | +}; |
| 46 | + |
| 47 | +struct F |
| 48 | +{ |
| 49 | + void operator()(M m) {} |
| 50 | +} f; |
| 51 | +</pre></blockquote> |
| 52 | +<p> |
| 53 | +The call `f(M{})` will print only "`Default`" but `bind_front(f)(M{})` is |
| 54 | +required to produce "`Default`" and "`Move`". We should allow |
| 55 | +implementations to elide the move operations, but not require it. |
| 56 | +</p> |
| 57 | +</discussion> |
| 58 | + |
| 59 | +<resolution> |
| 60 | +<p> |
| 61 | +This wording is relative to <paper num="N5014"/>. |
| 62 | +</p> |
| 63 | + |
| 64 | +<ol> |
| 65 | + |
| 66 | +<li><p>Modify <sref ref="[func.require]"/> as indicated:</p> |
| 67 | + |
| 68 | +<blockquote> |
| 69 | +<p> |
| 70 | +-3- Every call wrapper (<sref ref="[func.def]"/>) meets the <i>Cpp17MoveConstructible</i> and |
| 71 | +<i>Cpp17Destructible</i> requirements. An <i>argument forwarding call wrapper</i> is a call wrapper |
| 72 | +that can be called with an arbitrary argument list and delivers the arguments to the target object |
| 73 | +<del>as references</del>. This forwarding step delivers <del>rvalue arguments as rvalue references |
| 74 | +and lvalue arguments as lvalue references.</del><ins>:</ins> |
| 75 | +</p> |
| 76 | +<ol style="list-style-type: none"> |
| 77 | +<li><p><ins>(3.?) — lvalue arguments as lvalues,</ins></p></li> |
| 78 | +<li><p><ins>(3.?) — xvalue arguments as xvalues,</ins></p></li> |
| 79 | +<li><p><ins>(3.?) — prvalue arguments as either prvalues or xvalues</ins></p></li> |
| 80 | +</ol> |
| 81 | +</blockquote> |
| 82 | + |
| 83 | +</li> |
| 84 | + |
| 85 | +</ol> |
| 86 | +</resolution> |
| 87 | + |
| 88 | +</issue> |
0 commit comments