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

Lars T. Kyllingstad via Digitalmars-d digitalmars-d at puremagic.com
Sat Feb 13 13:41:06 PST 2016


On Saturday, 13 February 2016 at 19:25:37 UTC, Ola Fosheim 
Grøstad wrote:
> On Saturday, 13 February 2016 at 17:47:54 UTC, Lars T. 
> Kyllingstad wrote:
>> [...]
>
> C++ does indeed put the burden on the library programmer and is 
> not a good language for "non-professional" use. But it is 
> flexible by providing the mechanisms in the type system rather 
> than an opinionated solution. [...]

D is all about opinionated solutions. :)  In fact, I would go so 
far as to say that's what sets D apart from C++, and mostly in a 
good way.


> [...]
>
>> Here, you have unnecessary construction of C's members in the 
>> constructor which may or may not be optimised away before the 
>> assignment.
>
> Well, doing a swap would break the expectations for 
> assignment...

Whose expectations?  The formal expectation, as per the C++ 
standard, is that the moved-from object be left in a "valid but 
unspecified state".  Basically, as long as it is safe to destroy 
or reassign to the moved-from object, you're good.

I hope this is not coming across as me endorsing the practice of 
implementing move assignment in terms of swap, because I don't.  
But it *is* a rather common practice, enough so that Scott Meyers 
felt the need to write an article about it:

http://scottmeyers.blogspot.no/2014/06/the-drawbacks-of-implementing-move.html


>>  Furthermore, you have an unnecessary number of moves in the 
>> assignment operator -- plus the potential drawbacks of 
>> deferred release of the resource.
>
> I don't understand what you mean by unnecessary moves? 
> std::move/std::forward are just type casting so they don't 
> result in code...

A swap is three moves -- actual moves.


>> 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?
>
> Well, but that is like saying that C++03 also had move 
> semantics. There is nothing special about D's move(), it's just 
> a library function?

What is special is D's requirement that structs be movable by a 
raw bit blit, which again enables our particular library 
implementation of move().

C++ has no such requirement; for example it is perfectly OK for 
an on-stack C++ object to contain a pointer to itself.  A D-like 
move() on such an object would just produce mayhem.


>>> 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?
>
> The std.move() actually does a copy, then copy the init value 
> to the original. If something happens that prevents the value 
> from being preserved the object will be destroyed by the 
> destructors. I.e. an exception.

If you are still talking about this case:

     fun(move(someResource));

where fun() throws, like Sönke I don't have a big problem with 
the resource being lost there.  Given D's semantics, this is what 
I would expect.  And in D, at least you *know* it is lost, and it 
is easy to understand and explain to users.  In C++ you have no 
idea whether the resource is lost -- it depends on when the 
actual move operation happens and when the exception is thrown.


> And worse, if you have back pointers to it, it will end up 
> being inconsistent. There is no way the type system can prevent 
> back pointers without preventing D from being usable as a 
> language. Since you no longer have the original object... shit 
> can happen. In C++ you can set a mutex in the object and fix 
> things because you have the full object. So if someone tries to 
> follow the back pointer the mutex will block. You can probably 
> come up with many other scenarios. "postblit" does not fix this 
> (not a very elegant solution IMO).

Still not following you.  Postblit is not involved in a move at 
all -- that's what makes it a move.

If an exception is thrown, the object's destructor is called, and 
it would be the destructor's responsibility to break all ties to 
other objects.


> [...]



More information about the Digitalmars-d mailing list