digitalmars.D - Settling rvalue to (const) ref parameter binding once and for all
- "martin" <kinke libero.it> Nov 09 2012
- Manu <turkeyman gmail.com> Nov 10 2012
- "martin" <kinke libero.it> Nov 10 2012
Hi guys,
I hope you don't mind that I'm starting yet another thread about
this tedious issue, but I think the other threads are too clogged.
Let me summarize my (final, I guess) proposal. I think it makes
sense to compare it to C++ in order to anticipate and hopefully
invalidate (mainly Andrei's) objections.
parameter type | lvalue | rvalue
| C++ D | C++ D
------------------------|-------------|------------
T | copy copy | copy move
T& / ref T | ref ref | n/a n/a
out T (D only) | ref | n/a
T&& (C++ only) | n/a | move
auto ref T (D only) (*) | ref | ref
------------------------|-------------|------------
const T | copy copy | copy move
const T& / const ref T | ref ref | ref ref (*)
const T&& (C++ only) | n/a | move
(*): proposed additions
For lvalues in both C++ and D, there are 2 options: either copy
the argument (pass-by-value) or pass it by ref. There's no real
difference between both languages except for D's additional 'out'
keyword and, with the proposed 'auto ref' syntax, an (imo
negligible) ambiguity between 'ref T' and 'auto ref T' in D.
Rvalues are a different topic though. There are 3 possibilites in
general: copy, move and pass by ref. Copying rvalue arguments
does not make sense - the argument won't be used by the caller
after the invokation, so a copy is redundant and hurts
performance. D corrects this design flaw of C++ (which had to
introduce rvalue refs to add move semantics on top of the default
copy semantics) and therefore only supports moving instead. C++
additionally supports pass-by-ref of rvalues to const refs, but
not to mutable refs. I propose to allow pass-by-ref to both const
(identical syntax as C++, it's perfectly safe and logical) and
mutable refs (new syntax with 'auto ref' to emphasize that the
parameter may be an rvalue reference, with related consequences
such as potentially missing side effects).
Regarding the required overloading priorities for the proposed
additions to work properly, I propose:
1) lvalues: prefer pass-by-ref
so: ref/out T -> auto ref T (*) -> const ref T -> (const) T
- const lvalues: const ref T -> (const) T
- mutable lvalues: ref/out T -> auto ref T (*) -> const ref T
->
(const) T
2) rvalues: prefer pass-by-value (moving: argument allocated
directly on callee's stack (parameter) vs. pointer/reference
indirection implied by pass-by-ref)
so: (const) T -> auto ref T (*) -> const ref T (*)
Finally, regarding templates, I'm in favor of dropping the
current 'auto ref' semantics and propose to simply adopt the
proposed semantics for consistency and simplicity and to avoid
excessive code bloating. That shouldn't break existing code I
hope (unless parameters have been denoted with 'const auto ref
T', which would need to be changed to 'const ref T').
---
Before posting concerns about a perceived unsafety of binding
rvalues to 'const ref' parameters, please try to find a plausible
argument as to why the following is currently allowed:
void foo(const ref T x);
if (condition)
{
T tmp;
foo(tmp);
} // destruction of tmp
but the following shortcut, eliminating 3 lines (depending on
code formatting preferences ;)) and avoiding the pollution of the
local namespace with a 'tmp' variable, shouldn't be allowed:
if (condition)
foo(T()); // rvalue destructed immediately after the call
---
Let me also illustrate a deterministic allocation/destruction
scheme for the compiler implementation/language specification:
void foo(auto/const ref T a, auto/const ref T b);
foo(T(), T());
/* order:
1) allocate argument a on caller's stack
2) allocate argument b on caller's stack
3) invoke foo() and pass the argument addresses (refs)
4) destruct b
5) destruct a
*/
I guess something like that is covered by the C++ specification
for binding rvalues to const refs.
---
Now please go ahead and shoot. :)
Nov 09 2012
--14dae9cdc76bbde25c04ce220b95 Content-Type: text/plain; charset=UTF-8 Hear hear! I have dreams at night that look exactly like this proposal! :) I think I had one just last night, and woke up with a big grin on my face...2) rvalues: prefer pass-by-value (moving: argument allocated
indirection implied by pass-by-ref) Is this actually possible? Does the C/C++ ABI support such an action? GDC and LDC use the C ABI verbatim, so can this work, or will they have to, like usual, allocate on the caller's stack, and pass the ref through. I don't really see a significant disadvantage to that regardless. On 9 November 2012 20:05, martin <kinke libero.it> wrote:Hi guys, I hope you don't mind that I'm starting yet another thread about this tedious issue, but I think the other threads are too clogged. Let me summarize my (final, I guess) proposal. I think it makes sense to compare it to C++ in order to anticipate and hopefully invalidate (mainly Andrei's) objections. parameter type | lvalue | rvalue | C++ D | C++ D ------------------------|-----**--------|------------ T | copy copy | copy move T& / ref T | ref ref | n/a n/a out T (D only) | ref | n/a T&& (C++ only) | n/a | move auto ref T (D only) (*) | ref | ref ------------------------|-----**--------|------------ const T | copy copy | copy move const T& / const ref T | ref ref | ref ref (*) const T&& (C++ only) | n/a | move (*): proposed additions For lvalues in both C++ and D, there are 2 options: either copy the argument (pass-by-value) or pass it by ref. There's no real difference between both languages except for D's additional 'out' keyword and, with the proposed 'auto ref' syntax, an (imo negligible) ambiguity between 'ref T' and 'auto ref T' in D. Rvalues are a different topic though. There are 3 possibilites in general: copy, move and pass by ref. Copying rvalue arguments does not make sense - the argument won't be used by the caller after the invokation, so a copy is redundant and hurts performance. D corrects this design flaw of C++ (which had to introduce rvalue refs to add move semantics on top of the default copy semantics) and therefore only supports moving instead. C++ additionally supports pass-by-ref of rvalues to const refs, but not to mutable refs. I propose to allow pass-by-ref to both const (identical syntax as C++, it's perfectly safe and logical) and mutable refs (new syntax with 'auto ref' to emphasize that the parameter may be an rvalue reference, with related consequences such as potentially missing side effects). Regarding the required overloading priorities for the proposed additions to work properly, I propose: 1) lvalues: prefer pass-by-ref so: ref/out T -> auto ref T (*) -> const ref T -> (const) T - const lvalues: const ref T -> (const) T - mutable lvalues: ref/out T -> auto ref T (*) -> const ref T -> (const) T 2) rvalues: prefer pass-by-value (moving: argument allocated directly on callee's stack (parameter) vs. pointer/reference indirection implied by pass-by-ref) so: (const) T -> auto ref T (*) -> const ref T (*) Finally, regarding templates, I'm in favor of dropping the current 'auto ref' semantics and propose to simply adopt the proposed semantics for consistency and simplicity and to avoid excessive code bloating. That shouldn't break existing code I hope (unless parameters have been denoted with 'const auto ref T', which would need to be changed to 'const ref T'). --- Before posting concerns about a perceived unsafety of binding rvalues to 'const ref' parameters, please try to find a plausible argument as to why the following is currently allowed: void foo(const ref T x); if (condition) { T tmp; foo(tmp); } // destruction of tmp but the following shortcut, eliminating 3 lines (depending on code formatting preferences ;)) and avoiding the pollution of the local namespace with a 'tmp' variable, shouldn't be allowed: if (condition) foo(T()); // rvalue destructed immediately after the call --- Let me also illustrate a deterministic allocation/destruction scheme for the compiler implementation/language specification: void foo(auto/const ref T a, auto/const ref T b); foo(T(), T()); /* order: 1) allocate argument a on caller's stack 2) allocate argument b on caller's stack 3) invoke foo() and pass the argument addresses (refs) 4) destruct b 5) destruct a */ I guess something like that is covered by the C++ specification for binding rvalues to const refs. --- Now please go ahead and shoot. :)
--14dae9cdc76bbde25c04ce220b95 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable <div>Hear hear!<br></div><div>I have dreams at night that look exactly like= this proposal! :)<div><div>I think I had one just last night, and woke up = with a big grin on my face...<br><div><br></div><div>>=C2=A0<span style= =3D"font-family:arial,sans-serif;font-size:13px">2) rvalues: prefer pass-by= -value (moving: argument allocated</span></div> <span style=3D"font-family:arial,sans-serif;font-size:13px">=C2=A0 =C2=A0di= rectly on callee's stack (parameter) vs. pointer/reference</span><br st= yle=3D"font-family:arial,sans-serif;font-size:13px"><span style=3D"font-fam= ily:arial,sans-serif;font-size:13px">=C2=A0 =C2=A0indirection implied by pa= ss-by-ref)</span><div> <br></div><div><font face=3D"arial, sans-serif">Is this actually possible?<= /font></div><div><font face=3D"arial, sans-serif">Does the C/C++ ABI suppor= t such an action? GDC and LDC use the C ABI verbatim, so can this work, or = will they have to, like usual, allocate on the caller's stack, and pass= the ref through. I don't really see a significant disadvantage to that= regardless.</font></div> </div></div></div><div class=3D"gmail_extra"><br><br><div class=3D"gmail_qu= ote">On 9 November 2012 20:05, martin <span dir=3D"ltr"><<a href=3D"mail= to:kinke libero.it" target=3D"_blank">kinke libero.it</a>></span> wrote:= <br><blockquote class=3D"gmail_quote" style=3D"margin:0 0 0 .8ex;border-lef= t:1px #ccc solid;padding-left:1ex"> Hi guys,<br> <br> I hope you don't mind that I'm starting yet another thread about th= is tedious issue, but I think the other threads are too clogged.<br> <br> Let me summarize my (final, I guess) proposal. I think it makes sense to co= mpare it to C++ in order to anticipate and hopefully invalidate (mainly And= rei's) objections.<br> <br> =C2=A0 =C2=A0 =C2=A0parameter type =C2=A0 =C2=A0 | =C2=A0 lvalue =C2=A0 =C2= =A0| =C2=A0 =C2=A0rvalue<br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 | C++ =C2=A0 =C2=A0 D =C2=A0 | C++ =C2=A0 =C2=A0 D<br> ------------------------|-----<u></u>--------|------------<br> T =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 | copy =C2=A0 copy | copy =C2=A0 move<br> T& / ref T =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0| ref =C2=A0= =C2=A0ref =C2=A0| n/a =C2=A0 =C2=A0n/a<br> out T (D only) =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0| =C2=A0 =C2=A0 =C2=A0 =C2= =A0ref =C2=A0| =C2=A0 =C2=A0 =C2=A0 =C2=A0n/a<br> T&& (C++ only) =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0| n/a =C2=A0 =C2= =A0 =C2=A0 =C2=A0 | move<br> auto ref T (D only) (*) | =C2=A0 =C2=A0 =C2=A0 =C2=A0ref =C2=A0| =C2=A0 =C2= =A0 =C2=A0 =C2=A0ref<br> ------------------------|-----<u></u>--------|------------<br> const T =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 | copy =C2= =A0 copy | copy =C2=A0 move<br> const T& / const ref T =C2=A0| ref =C2=A0 =C2=A0ref =C2=A0| ref =C2=A0 = =C2=A0ref (*)<br> const T&& (C++ only) =C2=A0 =C2=A0| n/a =C2=A0 =C2=A0 =C2=A0 =C2=A0= | move<br> <br> (*): proposed additions<br> <br> For lvalues in both C++ and D, there are 2 options: either copy the argumen= t (pass-by-value) or pass it by ref. There's no real difference between= both languages except for D's additional 'out' keyword and, wi= th the proposed 'auto ref' syntax, an (imo negligible) ambiguity be= tween 'ref T' and 'auto ref T' in D.<br> <br> Rvalues are a different topic though. There are 3 possibilites in general: = copy, move and pass by ref. Copying rvalue arguments does not make sense - = the argument won't be used by the caller after the invokation, so a cop= y is redundant and hurts performance. D corrects this design flaw of C++ (w= hich had to introduce rvalue refs to add move semantics on top of the defau= lt copy semantics) and therefore only supports moving instead. C++ addition= ally supports pass-by-ref of rvalues to const refs, but not to mutable refs= . I propose to allow pass-by-ref to both const (identical syntax as C++, it= 's perfectly safe and logical) and mutable refs (new syntax with 'a= uto ref' to emphasize that the parameter may be an rvalue reference, wi= th related consequences such as potentially missing side effects).<br> <br> Regarding the required overloading priorities for the proposed additions to= work properly, I propose:<br> 1) lvalues: prefer pass-by-ref<br> =C2=A0 =C2=A0so: ref/out T -> auto ref T (*) -> const ref T -> (co= nst) T<br> =C2=A0 =C2=A0- const lvalues: =C2=A0 const ref T -> (const) T<br> =C2=A0 =C2=A0- mutable lvalues: ref/out T -> auto ref T (*) -> const = ref T -><br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 (const) T<br> 2) rvalues: prefer pass-by-value (moving: argument allocated<br> =C2=A0 =C2=A0directly on callee's stack (parameter) vs. pointer/referen= ce<br> =C2=A0 =C2=A0indirection implied by pass-by-ref)<br> =C2=A0 =C2=A0so: (const) T -> auto ref T (*) -> const ref T (*)<br> <br> Finally, regarding templates, I'm in favor of dropping the current '= ;auto ref' semantics and propose to simply adopt the proposed semantics= for consistency and simplicity and to avoid excessive code bloating. That = shouldn't break existing code I hope (unless parameters have been denot= ed with 'const auto ref T', which would need to be changed to '= const ref T').<br> <br> ---<br> <br> Before posting concerns about a perceived unsafety of binding rvalues to &#= 39;const ref' parameters, please try to find a plausible argument as to= why the following is currently allowed:<br> <br> void foo(const ref T x);<br> if (condition)<br> {<br> =C2=A0 =C2=A0 T tmp;<br> =C2=A0 =C2=A0 foo(tmp);<br> } // destruction of tmp<br> <br> but the following shortcut, eliminating 3 lines (depending on code formatti= ng preferences ;)) and avoiding the pollution of the local namespace with a= 'tmp' variable, shouldn't be allowed:<br> <br> if (condition)<br> =C2=A0 =C2=A0 foo(T()); // rvalue destructed immediately after the call<br> <br> ---<br> <br> Let me also illustrate a deterministic allocation/destruction scheme for th= e compiler implementation/language specification:<br> <br> void foo(auto/const ref T a, auto/const ref T b);<br> <br> foo(T(), T());<br> /* order:<br> =C2=A0 =C2=A01) allocate argument a on caller's stack<br> =C2=A0 =C2=A02) allocate argument b on caller's stack<br> =C2=A0 =C2=A03) invoke foo() and pass the argument addresses (refs)<br> =C2=A0 =C2=A04) destruct b<br> =C2=A0 =C2=A05) destruct a<br> */<br> <br> I guess something like that is covered by the C++ specification for binding= rvalues to const refs.<br> <br> ---<br> <br> Now please go ahead and shoot. :)<br> </blockquote></div><br></div> --14dae9cdc76bbde25c04ce220b95--
Nov 10 2012
On Saturday, 10 November 2012 at 18:20:56 UTC, Manu wrote:Hear hear! I have dreams at night that look exactly like this proposal! :) I think I had one just last night, and woke up with a big grin on my face...
I'm glad I'm not alone on this. :)2) rvalues: prefer pass-by-value (moving: argument allocated
indirection implied by pass-by-ref) Is this actually possible?
It surely is and would only require the compiler to map the location of the parameter in the callee's future stack frame to the caller's stack frame. I think this is how DMD implements it.Does the C/C++ ABI support such an action? GDC and LDC use the C ABI verbatim, so can this work, or will they have to, like usual, allocate on the caller's stack, and pass the ref through. I don't really see a significant disadvantage to that regardless.
I'm not sure how C++ does it, I was wondering about that too actually after posting this. It quite possibly uses real references for '(const) T&&' (otherwise how would you be able to transform a passed lvalue reference to an rvalue reference via std::move() without copying it?), so my table may be incorrect for these 2 cells, although that wouldn't change anything really for the proposal, unless someone showed a use case for 'const T&&' where 'const T&' doesn't fit (I certainly can't think of any).
Nov 10 2012









Manu <turkeyman gmail.com> 