On Thu, 3 Oct 2024 at 04:06, Walter Bright via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
On 10/1/2024 1:06 PM, Timon Gehr wrote:
> I think in case we did go the function route, I think any implementation of
> `move` that is much more complex than the following is a failure:
>
> ```d
> auto move(T)(@moved T arg)=>arg;
> ```

The major difference between @move and __rvalue is the former is attached to the
parameter, and the latter is attached to the argument. This might seem a
distinction without a difference, but this has large implications with how
overloading works.

For example, how do we distinguish a move constructor from a copy constructor?
```
this(ref S); // copy constructor
this(S);     // move constructor
S s = t;     // calls copy constructor (lvalue)
S s = f();   // calls move constructor (rvalue)
```
The current overloading rules work out of the box, an rvalue goes for the move
constructor, and an lvalue goes to the copy constructor.

The problem here is when I want to move t into s. How do I get it to call the
move constructor?
```
S move(ref S s) { return s; } // convert argument from lvalue to rvalue
S s = move(t);
```
This works, however, it creates a copy of s and then moves the copy! There needs
to be a way to tell the compiler to use the move construct, hence:
```
S s = __rvalue(t);
```
All __rvalue does is flag the expression in ( ) as an rvalue. Then the rest of
the semantics go from there. Note that a struct parameter with a move
constructor will always pass by ref regardless, which is what we want here.
Also, move semantics will only work on structs. Not classes, integers, pointers,
arrays, etc. If move semantics are desired for them, they'll need to be wrapped
in a struct, or use a template to wrap it for you.

Consider:
```
this(ref S);   // copy constructor
this(@move S); // move constructor
```
I don't know how to make overloading work with this.

It's always some weird little detail that changes when I feel like we discussed/designed a thing and then you implement it! :P
your __rvalue() is essentially the `T move(ref T)` intrinsic we discussed at length; so why not just make the move intrinsic rather than this thing? It seems trivial, but the reason I say this (as we discussed at length), is that in addition to the trivial act of stripping away the ref to make it an rvalue, the move expression will inevitably need to be enhanced with lifetime tracking semantics. In the future, you will want to put logic in that intrinsic to mark the end of lifetime of the memory region, and possibly mark the transition of ownership for ownership tracking... 'move' is the operation we seek, and it has lifetime related semantics involved with the operation. __rvalue() doesn't feel like the right abstraction for that set of semantics; you're gonna find yourself wondering where to pin the lifetime semantics in the future...

Timon: Do you have any comment? Am I wrong?