Discussion: Rvalue refs and a Move construtor for D

kinke noone at nowhere.com
Sat Sep 7 12:04:49 UTC 2019

On Friday, 6 September 2019 at 20:24:24 UTC, Suleyman wrote:
> On Friday, 6 September 2019 at 17:59:11 UTC, kinke wrote:
>> The problem here is the extended lifetime of the by-value 
>> parameter. Example:
>> T global;
>> void foo(T param) { /* make `param` own a huge buffer */ }
>> void caller()
>> {
>>     foo(move(global));
>>     // without destructing and resetting to T.init, `param` 
>> continues to live
>>     // and keeps the huge buffer alive, until `global` is 
>> reassigned or destructed
>> }
> I see no problem with that. That is valid behavior with rvalue 
> ref. You're trying to get the benefit of rvalue ref you can't 
> get the full benefit without the draw backs. There is no in 
> middle solution, either pass by ref or call the move 
> constructor.

I never meant to make such a huge breaking change to existing 
semantics (by-val param possibly not destructed when going out of 
scope), just to optimize passing moved lvalues to by-value params 
by only doing an implicit 'half move' (no allocated temporary, no 
move ctor call, just destruct and then reset to T.init). I'm not 
sure this 'middle solution' is really feasible either, that's why 
we are discussing.

Wrt. rvalue ref params and return 'values', maybe we could get 
away without introducing `@rvalue ref` by changing the current 
`auto ref` semantics for params (and return 'values') to what you 
proposed - an l/rvalue ref, i.e., a reference in both cases, just 
annotated with rvalue-ness, and never a value anymore. Then, your 

> @rvalue ref get(@rvalue ref S arg) { return arg; }
> void foo(@rvalue ref S);

can be expressed as:

auto ref get()(auto ref S arg) { return forward(arg); }
void foo()(auto ref S s);

In the (unlikely?) case you really only want to be called with an 
rvalue ref, you could use a template constraint like

void foo()(auto ref S s)
   if (__traits(isRvalueRef, s));

The semantics of an `auto ref` param would still need to be 
clarified in the rvalue case - is it seen as an rvalue ref 
(breaking change!) or as a value? I.e.:

void sink(S s);

void seenAsRvalueRef()(auto ref S s)
     // considering rvalue case only:
     sink(s); // automatically moved
     sink(s); // automatically moved again (but probably reset to 
S.init above)

void seenAsValue()(auto ref S s)
     sink(s); // passed by value, i.e., copy
     sink(forward(s)); // explicitly moved

I'd prefer the latter value-view, as that's compatible with 
current semantics.

More information about the Digitalmars-d mailing list