On Sat, 12 Oct 2024 at 04:20, Jonathan M Davis via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
On Monday, September 30, 2024 10:05:16 AM MDT Walter Bright via Digitalmars-d
wrote:
> I've been implementing move constructors. They are working, at least on my
> test cases so far. They are distinguished by a copy constructor takes its
> argument by ref, and a move constructor by value:
>
> ```
> struct S
> {
>      this(ref S); // copy constructor
>      this(S);     // move constructor
> }
> ```
> So far, so good. Consider:
> ```
> void phone(S s)
> {
>      S t = s;  // copy constructor
> }
> ```
> But what if we want to initialize `t` via a move constructor? Somehow, `s`
> has to be converted from an lvalue to an rvalue. This is done via the
> function rvalue(): ```

Given the we're now looking at having a separate syntax for move
constructors, I would argue that they should just have to be ref, which
should eliminate the special cases that you're fighting here.

You really need to re-read the DIP here and understand the design principle of this whole thing.
This suggestion show us that you either don't understand the DIP, totally missed the point, or possibly that you fundamentally disagree with the DIP; and if that's the case, I think you need to present that argument and what you'd propose instead, rather than talk with respect to implementation of the DIP with a completely different idea in mind. If you change the fundamental substance of the DIP, it's a new DIP.

The proposal in the DIP is very simple; struct rvalues are ref too now, so don't worry about the ref stuff; everything's a ref. The problem to be solved is, how do we appropriately distinguish an rvalue from an lvalue; and while we've had a myriad of proposals adding attributes, Walter found an arrangement where the distinction can be expressed in existing language in an astonishingly elegant way; recognise that by-value calls (accepts rvalue) are all actually move opportunities.

void f(ref T) // arg is an lvalue; the syntax says "I have received a reference to someone else's thing"; or put another way, the callee does NOT own the argument.
void f(T) // arg is an rvalue; this syntax says "I have received this thing"; the callee owns the argument, and as such, is a valid recipient of any move operation.
In order to make move operations actually move operations, they need to be passed by ref (by rvalue-ref, so to speak), and that is literally the entire point of the DIP; the calling convention is adjusted so a by-value (r-value) parameter is passed by rvalue-ref.

This is what I mean where I say we're talking about "move semantics", but everyone seems to be fixated on move constructors as if they're an extraordinarily interesting part of this story.

A move constructor is just an efficient initialisation opportunity, and it's a small part of the overall story regarding move semantics.

As I see it; the move constructor must be an overload; otherwise, surely you must assume the overload selection rules don't work, and so every single other function (aside from the move constructor which received a blessed hack!) will not make a proper selection.
Move semantics ARE proper overload selection, that's the entire meat of this design. This design IS overload selection rules.... to try and work that problem off to the side would be to have completely missed the point. If you exclude overload resolution from the picture, then I don't even know what we're talking about here.