Move Constructor Syntax

Timon Gehr timon.gehr at gmx.ch
Thu Oct 17 17:48:02 UTC 2024


On 10/17/24 16:56, Manu wrote:
> On Thu, 17 Oct 2024, 23:16 Timon Gehr via Digitalmars-d, <digitalmars- 
> d at puremagic.com <mailto:digitalmars-d at puremagic.com>> wrote:
> 
>     On 10/17/24 02:05, Manu wrote:
>      > On Thu, 17 Oct 2024, 07:36 Timon Gehr via Digitalmars-d,
>     <digitalmars-
>      > d at puremagic.com <mailto:d at puremagic.com> <mailto:digitalmars-
>     d at puremagic.com <mailto:digitalmars-d at puremagic.com>>> wrote:
>      > ...
>      >
>      >     I agree with Manu's reasoning why having `this(ref S)` and
>     `this(S)`
>      >     work as "initialization from lvalue" and "initialization from
>     rvalue",
>      >     corresponding to copy and move respectively would be cleaner.
>      >
>      >     But overall I don't have a strong preference, as dedicated
>     syntax also
>      >     has some benefits. The thing I think is most important is
>     getting the
>      >     semantics right.
>      >
>      >
>      > Special-casing the constructor is just admitting that the overload
>      > selection mechanics are broken for **all other function
>     calls**... It's
>      > not a matter of 'preference'; if the overload mechanics work
>     properly
>      > then special casing the constructor wouldn't even be thinkable or
>     raised
>      > in conversation. It's a hack; a really dumb hack which just
>     leaves EVERY
>      > OTHER FUNCTION broken.
>      >
>      > The fact you have "no strong preference" surprises me, maybe I've
>      > misunderstood some way in which this is not a hack that's totally
>      > broken?
> 
>     There is a tradeoff. I think a `this(S)` has to call the destructor of
>     the argument at the end of its scope. This is the right thing to do for
>     normal functions, but you might expect a move constructor to not
>     call it
>     as it already destroys it as part of its normal operation. DIP1040
>     tried
>     to fix this by _special-casing the constructor_. ;)
> 
>     With a syntax based on `ref`, it is clear that there will not be a
>     destructor call.
> 
> 
> But upon very trivial investigation we quickly determined that all 
> arguments need to be cleaned up; it avoids complicated special cases and 
> additional attribution.

`ref` arguments do not need to be cleaned up by the caller, and the 
callee has more information about whether cleanup is required.

With `this(S)`, you get one or two destructor calls on dummy objects for 
an explicit move by default, with `=this(ref S)` it is zero or one.

> If there's an opportunity for destructor 
> elision, it would be an advanced optimisation. It shouldn't be the basic 
> semantic for simplicity's sake... and when you accept that, any 
> complexity around the design disappears completely.
> ...

Don't get me wrong, I prefer `this(S)`, `this(ref S)`, `opAssign(S)`, 
`opAssign(ref S)`. It's just not a strong preference. I also agree it is 
simpler. Your preference may be more pronounced than mine if you place a 
higher emphasis on simplicity.

> 
>      > Can you explain to me how every other function call isn't broken
>      > under the special-case-for-move-constructor solution?
> 
>     Move semantics still needs a separate solution, but this thread is
>     about
>     move constructors. Move constructors are not needed for move semantics,
>     they are needed to manually hook moves that involve a transfer of
>     values
>     between different memory locations.
> 
> 
> They're not 'a thing' though, they are just a natural consequence of 
> move semantics. Move construction will work naturally given an rvalue 
> constructor, no special cases, no rules, probably not even one single 
> line of code in the compiler is necessary once move semantics are 
> working generally.
> ...

Well, this is not true. The compiler sometimes moves things implicitly, 
which may need a call to the move constructor. Of course, the best way 
to implement this would be to have the compiler use `__move` internally, 
which is then lowered to a move constructor call if needed.

I have given this example a number of times:

```d
S foo(bool x){
     S a,b;
     if(x) return a;
     return b;
}
```

The return value of this function has its own address, so `a` and `b` 
need to be moved. The compiler needs to call the move constructor here.


Another thing is:


```d
struct T{
     this(T t){ ... }
}

struct S{
     T t;
     int x;
     // (no move constructor)
}

void main(){
     S s0;
     S s1=move(s0); // must call move constructor for T
}
```

Also, the compiler has to detect that `T` and `S` are non-POD types as 
those have a different ABI.

> I think this is where it's gotten lost...
> Move semantics don't need a "separate solution", they are actually the 
> conversation we're meant to be having.
> ...

That's the same thing. It's a different conversation. I agree it is more 
important, but the issue is I think a lot of people think move semantics 
just means move construction and assignment, where addresses change, 
when it is also about cases where addresses do not necessarily change, 
such as forwarding.


> This talk about move constructors is where it's all gotten lost in the 
> woods; If move semantics exist, then move constructors don't need "a 
> solution" at all, they just work naturally with no further intervention.
> ...

Well, this is not true.

> I'm pretty sure that confusion is the basis for the madness going on 
> here... people seem to be obsessed with move construction while 
> completely ignoring or overlooking move semantics in general, or somehow 
> thinking it's separate or secondary?
> ...

Some probably think it is the same thing. Anyway, this thread is 
actually about move constructors, so it seems natural that people talk 
about move constructors in this thread.

Martin has actually opened a thread about the required steps to achieve 
move semantics. For some reason it is getting less attention.

> So, to belabour the point... move semantics is the primary concept here; 
> and there should be no "move constructor" as a distinct feature, it's 
> just a natural consequence of move semantics existing. No conversation 
> about move constructors is necessary... this is all just leading to 
> confusion.
> ...

Well, I don't think this is true. Just like the copy constructor needs a 
little language support, so does the move constructor. Or it just will 
not be called in some situations where a call is needed.

> Reframe thought experiments in terms of `void f(ref T)`, and `void 
> f(T)`, hopefully that should eliminate confusion. Select the proper 
> overload in that set when calling f(...), and we're done here.
> ...

Well, that part is important, but it is not everything.

> Please, everyone stop talking about "move constructors"... at least 
> until you've understood what move semantics are. Get your head around 
> move semantics, then you will naturally understand how redundant and 
> misleading this entire conversation about move constructors is...
> ...

Idk if it is redundant. Anyway, there is Martin's thread. I am happy to 
discuss further there as well.

> 
>      > Overload selection
>      > has to work, it is basically the meat of this whole thing...
>     there's not
>      > really anything else to it.
>      >
>      > Broken calling semantics for every function other than the
>     constructor
>      > is not a 'compromise', it baffles me that people would even
>     consider it.
>      > I mean, you don't 'call' constructors so often, but you do call
>      > everything else.
>      >
> 
>     I am completely with you here.
> 
> 
> I know, but once readers understand and accept this, they will also 
> understand that move constructors aren't "a thing", they're just a 
> normal constructor with a rvalue as argument, and calling semantics/ 
> overload selection is necessarily the same as any other function.
> ...

The language has to specify when and how the constructor is called 
implicitly. That's about the extent of it, but it still needs to be in 
the spec.

> I think this confusion and noise will all disappear as soon as people 
> understand this.
> 

Probably.


More information about the Digitalmars-d mailing list