Move Constructor Syntax

Jonathan M Davis newsgroup.d at jmdavisprog.com
Sun Oct 6 04:33:02 UTC 2024


On Saturday, October 5, 2024 10:04:28 PM MDT Walter Bright via Digitalmars-d 
wrote:
> ```
> struct S { ... }
>
> this(ref S) // copy constructor
> this(this)  // postblit
> this(S)     // move constructor
> ~this()     // destructor
>
> alias T = S;
> this(T);    // also move constructor
>
> alias Q = int;
> this(Q);    // regular constructor
> ```
> As the above illustrates, a move constructor cannot be distinguished from a
> regular constructor by syntax alone. It needs semantic analysis.
>
> While this seems simple enough, it isn't I have discovered to my chagrin.
> The overload rules in the language (including things like rvalue references
> where sometimes an rvalue becomes an lvalue) that the move constructors get
> confused with the copy constructors, leading to recursive semantic loops
> and other problems.
>
> I've struggled with this for days now.
>
> A fix that would simplify the language and the compiler would be to have a
> unique syntax for a move constructor, instead of the ambiguous one in the
> proposal. That way, searching for a copy constructor will only yield copy
> constructors, and searching for a move constructor will only yield move
> constructors. There will be a sharp distinction between them, even in the
> source code. (I have seen code so dense with templates it is hard to figure
> out what kind of constructor it is.)

Whatever the syntax is, I would definitely say that move constructors need a
unique syntax. At work, we've had constructors that took the same type so
that code such as

    auto a = A(otherA);

and

    auto a = new A(otherA);

would compile (though honestly, it would be nice if that syntax worked even
without an explict constructor when the types match). This comes up in
particular with variant types which can hold pretty much anything, in which
case, templated constructors hit it quite easily. It also comes up in
generic code where you're dealing with multiple types, and it's nice to not
have to use static if braches just so that you can make a copy without a
constructor call.

And when adding copy constructors to some types, having constructors which
took the same type caused grief, because for whatever reason, rvalue
constructors are disallowed if you have a copy constructor. So, we had to
create helper functions for constructing some types instead of being able to
reliably do

    auto a = A(otherA);

and we were forced to use static if branches in some cases to distinguish
between types.

I don't recall at the moment how auto ref interacts with any of this either,
but it probably is disallowed with a copy constructor, and it probably will
cause further issues if move constructors don't have unique syntax.

In any case, we've had constructors that are not necessarily supposed to be
move constructors which would match this(S), so changing this(S) to be a
move constructor would cause problems for existing code.

> Something like one of:
> ```
> 1. =this(S)
> 2. this(=S)
> 3. <-this(S)
> ```
> ?
>
> It may take a bit of getting used to. I kinda prefer (1) as it is sorta like
> `~this()`.

Honestly, I'd just argue for slapping @move on the constructor, but if I had
to pick one of those three, I'd probably go with #1 simply because it would
stand out more. Either way, I definitely wouldn't want #3, since it looks
too much like a mistyped pointer dereference in C++.

- Jonathan M Davis





More information about the Digitalmars-d mailing list