Discussion: Rvalue refs and a Move construtor for D

kinke noone at nowhere.com
Thu Aug 29 20:26:10 UTC 2019


On Thursday, 29 August 2019 at 19:04:45 UTC, Manu wrote:
> Our move semantics are slow (lots of copying

I fully agree and it's been bothering me for years too.

C++:

void callee(NoPOD s);
/* Low-level, this is actually `void callee(NoPOD &&s)`.
  * The caller is to allocate & set up the instance before the 
call,
  * pass a pointer to it and then destruct it after the call.
  */

void caller()
{
     // rvalue case:
     callee(NoPOD());
     /* actually:
     {
         NoPOD temp;
         callee((NoPOD &&) &temp);
     } // destruct temp
     */

     // lvalue case:
     NoPOD lvalueArg;
     callee(lvalueArg);
     /* actually:
     {
         NoPOD temp = lvalueArg; // full copy
         callee((NoPOD &&) &temp);
     } // destruct temp
     */

     // manual moving:
     callee(std::move(lvalueArg));
     /* actually:
     callee((NoPOD &&) &lvalueArg);
     */
}

D:

If we adopted the C++ ABI in this regard (avoid the stack for 
non-PODs, possibly for large PODs too, and use rvalue refs under 
the hood) and made `move` an intrinsic, the compiler could use it 
to elide the lvalue copy for arguments passed by value.

Similary, if `forward` was an intrinsic, the compiler could use 
it to propagate the lvalue-ness of `auto ref` parameters as 
arguments:

void callee(ref NoPOD s); // for lvalues
void callee(NoPOD s); // for rvalues, actually: `@rvalue ref 
NoPOD s`

void wrapper()(auto ref NoPOD s)
{
     callee(forward(s));
     /* actually:
     static if (__traits(isRef, s)) // void wrapper(ref NoPOD s)
     {
         // call lvalue overload, perfectly forwarding the `s` 
reference
         callee(s);
     }
     else // void wrapper(@rvalue ref NoPOD s)
     {
         // call rvalue overload, perfectly forwarding the `s` 
rvalue reference
         callee(s);
     }
}

void caller()
{
     // rvalue case:
     wrapper(NoPOD());
     /* actually:
     {
         NoPOD temp;
         wrapper(cast(@rvalue ref) temp); // just forwards the 
pointer to rvalue-version of callee
     } // destruct temp
     */

     // lvalue case:
     NoPOD lvalueArg;
     wrapper(lvalueArg); // just forwards the pointer to 
lvalue-version of callee

     // manual moving:
     wrapper(move(lvalueArg));
     /* actually:
     wrapper(cast(@rvalue ref) lvalueArg); // forwards the pointer 
to rvalue-version of callee
     */
}

I hope that covers enough use cases, so that we could get away 
with ABI change (which btw would also fix C++ interop wrt. 
passing non-PODs by value) + move/forward as intrinsics, without 
having to touch the language and introducing ugly `@rvalue ref`.


More information about the Digitalmars-d mailing list