D's equivalent to C++'s std::move?

Lars T. Kyllingstad via Digitalmars-d digitalmars-d at puremagic.com
Sat Feb 13 09:47:54 PST 2016


On Saturday, 13 February 2016 at 12:14:43 UTC, Ola Fosheim 
Grøstad wrote:
> On Saturday, 13 February 2016 at 09:11:06 UTC, Lars T. 
> Kyllingstad wrote:
>> In my experience, in the vast majority of cases a C++ move 
>> operation boils down to a memberwise copy (of value types) or 
>> copy-and-reset (of reference types).  With the extra logic and 
>> program flow that is sometimes involved in move construction 
>> and move assignment, I suspect that a straightforward double 
>> memcpy as it is done in D will be almost as performant or 
>> moreso most of the time.
>
> No? Not in the libraries I write. A move typically just means 
> copying 1-4 64 bit values and setting a single 64 bit value in 
> the source. Usually written as an inline function.

Not knowing anything about the libraries you write, it's hard to 
argue with that.  But I agree that given that you are in control 
of all the code AND can make the move ctor/assignment available 
for inlining (AND are an experienced programmer), then yes, you 
can most definitely get better performance with C++'s move() than 
with D's move().

But consider the more general case where you have an object of 
type 'struct A', which is embedded in an object of type 'struct 
B', which is again embedded in an object of type 'struct C', and 
so on, and where A, B, and C are perhaps in separate libraries or 
for some other reason their move ctors/assigments cannot be 
inlined.  Then, you are looking at multiple levels of function 
calls and you are also at the mercy of whoever wrote their move 
code.

In D the cost of a move is very predictable and should be 
performant enough for most use cases.  And for the ones where it 
absolutely isn't, I'm sure making a custom solution is feasible.


> Or nothing, in the case where the logic does not end up with a 
> move, something which D cannot represent with the same semantic 
> distinction.
>
>> Add to that the fact that a lot of programmers out there will 
>> implement move construction in terms of move assignment -- 
>> which makes it a default construction PLUS move -- and move 
>> assignment in terms of swap -- i.e., three moves -- for the 
>> sake of DRY.
>
> Huh? Move assignments is no different from move construction, 
> except you have to release the existing value if the receiving 
> object isn't empty. Constructing an empty resource owner object 
> usually just means setting a single field to zero, which is 
> inlined and removed if it is followed by an assignment.

What I meant is that you will find a lot of C++ code out there, 
written by well-meaning programmers, that looks like this:

class C
{
     C(C&& other)
     {
         operator=(std::move(other));
     }
     // and/or
     C& operator=(C&& other)
     {
         swap(*this, other);
         return *this;
     }
};

Here, you have unnecessary construction of C's members in the 
constructor which may or may not be optimised away before the 
assignment.  Furthermore, you have an unnecessary number of moves 
in the assignment operator -- plus the potential drawbacks of 
deferred release of the resource.

>>  Personally, I think D's move semantics are actually clearer 
>> and easier to get right.
>
> But I don't think D has move semantics. I don't think it makes 
> for correctness for resource ownership.

I'm not sure what you mean by "has move semantics" here.  It does 
not have C++'s move semantics, no, but I would say D has its own 
move semantics.  It has a move() function that transfers raw 
state between objects, and D structs are supposed to be designed 
so they are movable by means of raw bit transfer, allowing the 
compiler and GC to move them around as it sees fit.  But maybe 
I'm missing something?


>> explain to newbies:  If you use std.move() on something it 
>> definitely gets moved.  In C++, if you use std::move() on 
>> something it may or may not be moved; it depends on the 
>> recipient of the move.
>
> No? With D's std.move() the resource can be destroyed or get 
> into an inconsistent state if the caller does it wrong?

I guess this is what I don't understand.  How and when does that 
happen?

Lars


More information about the Digitalmars-d mailing list