Discussion: Rvalue refs and a Move construtor for D

Manu turkeyman at gmail.com
Mon Sep 2 00:03:14 UTC 2019

On Sun, Sep 1, 2019 at 7:10 AM Suleyman via Digitalmars-d
<digitalmars-d at puremagic.com> wrote:
> On Sunday, 1 September 2019 at 08:59:08 UTC, Les De Ridder wrote:
> > [...]
> > Maybe a solution with a UDA is possible?
> Possible.
> > I'm not sure why there are three different ways to 'cast to an
> > rvalue'?
> > Is there a technical reason?
> I'm just demonstrating the possibilities. Likely only one of them
> will be picked.
> > I also noticed you're casting lvalues to 'rvalues' and then
> > binding them a `ref` param. Is that not an rvalue reference?
> Yes but I would argue this is just a perk of rvalue ref and not
> what the essence of it is.
> You can do the same thing by assigning to a temporary then
> passing it by ref. This all the compiler does here.
> > Why can't we have generalised rvalue references instead?
> What do you need if for? Rvalue ref comes with it's own
> overloading and implicit conversion rules. Generalized rvalue ref
> is not absolutely needed for move semantics. What else comes to
> mind other than move semantics as a use case?

So, I think this PR is almost there WRT generalised rvalue ref's aswell.
This approach is a twofer; at least, with the tweak to use an
`@rvalue` attribute instead of a weird name.
If `this(@rvalue T)` only accepts rvalues, then as that interacts with
Andrei's DIP which he presented at dconf (available via `-preview`
today) which can pass rvalues to ref by implicit temporary, then this
naturally gives rvalue references by coincidence. It's encouraging
when language features fit together naturally.

So, I guess this is the suite of constructor-like functions that would
be technically possible:

A: this(T)
B: this(@rvalue T)
C: this(ref T)
D: this(@rvalue ref T)

So, consider what each of these functions can do:
  A: arg in an rvalue by-value - can accept an rvalue or an lvalue (by copying)
  B: arg is an rvalue by-value - I *guess* that this syntax would only
allow the function to accept rvalues as arguments unlike A
  C: arg is an lvalue by-ref - can accept lvalue, and with Andrei's
DIP `-preview`, can accept rvalue by implicit temporary
  D: arg is an rvalue by-ref - can accept rvalues only, by implicit
temporary as per Andrei's DIP.

C is the only case where the argument received is an lvalue, and as
such, we recognise this as a copy constructor.
What's interesting, is that all the others are theoretically valid
forms of move constructor... Hmm.
D is the most-desirable form of move constructor, but I don't see any
reason to *require* that form, they're all functionally valid.

Other interesting observations, is that A and B are potentially
redundant, but maybe useful in a rare case where you want your API to
explicitly reject lvalues. I can see some use on that; which we
describe that today with `@disable` tricks, but I think this is much
more obvious.

There are some ambiguities, or overload selection preferences here
that would need to be spec-ed:

Today, A and C can overload, with the rule that an lvalue prefers C
and an rvalue prefers A. I'll try and expand the complete set, cases
are where combinations of the functions above exist:

A: rvalue -> A, lvalue -> A
B: rvalue -> B, lvalue -> ERROR (not accept lvalue)
C: rvalue -> C*, lvalue -> C   (* note: rvalue's may call by implicit
temp as-per Andrei's DIP, but this is NOT a move operation, just a
D: rvalue -> D, lvalue -> ERROR (not accept lvalue)

AB: rvalue -> B, lvalue -> A
AC: rvalue -> A, lvalue -> C  (**existing rules today**)
AD: rvalue -> D, lvalue -> A
BC: rvalue -> B, lvalue -> C
BD: rvalue -> ERROR (ambiguous B/D), lvalue -> ERROR (no overload
accepts lvalues)
CD: rvalue -> D, lvalue -> C
ABC: rvalue -> B, lvalue -> C
ABD: rvalue -> ERROR (ambiguous B/D), lvalue -> A
ACD: rvalue -> D, lvalue -> C
ABCD: rvalue -> ERROR (ambiguous B/D), lvalue -> C

So the apparent rules from that table are:
  * With respect to lvalues, the above table shows no change from
existing rules (good)
  * rvalues prefer explicit `@rvalue`, otherwise fall back to existing
rules (seems right)
  * B & D are ambiguous overloads, and are an error if they both exist.

No change in existing rules is observed, so no chance of breakage there.

One additional observation, is that in the sets ABC, ACD, ABCD, while
the A overload is present, it's not selected in either the lvalue or
rvalue cases... so is it a redundant overload? This case needs to be

TL;DR, we don't need to define rvalue ref semantics explicitly here,
because they just emerge naturally in conjunction with Andrei's DIP.

More information about the Digitalmars-d mailing list