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

Ola Fosheim Grøstad via Digitalmars-d digitalmars-d at puremagic.com
Sat Feb 13 14:42:34 PST 2016


On Saturday, 13 February 2016 at 21:41:06 UTC, Lars T. 
Kyllingstad wrote:
> 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.

D1 was all about opinionated ready-made builtin solutions, but D2 
is supposedly meant to support generic programming and then the 
responsibility of "creating opinions" should be moved out of the 
language and into libraries.


> 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.

Hmm, do you have a reference (URL) to the "valid but unspecified 
state" part? I'm not quite sure what that refers to.


> 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:

I've never heard of it, and never thought it would be a good 
idea. Are you sure this is common?


> A swap is three moves -- actual moves.

If you are talking std::swap, probably. Never use it, so don't 
know if there is any measurable overhead.

If you are talking about the microcode in the CPU, then it 
typically takes 2 loads and 2 stores to swap two pointers on the 
heap, and the loads and stores can execute in parallel... So 
performance wise, not a big deal. But with debugging/consistency 
in mind you should set the source to nullptr instead.

I know programmers talk alot about swap being implemented as

tmp = a
a = b
b = a

But that is actually how it is specified it source code, not how 
it is implemented in running code. In the CPU it goes like this:

reg1 = load a; reg2 = load b
b = store reg1; a = store reg2

Setting it to null would be almost the same.

reg1 = load a; reg2 = 0
b = store reg1; a = store reg2

Unless you use special commands and zero out the entire cacheline 
of "a" you still get the same amount of cache-misses as well.


> 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.

Yes, but it isn't enforced by the compiler by static analysis, is 
it?  So D has no particular advantage to C++ for an object that 
is designed to be movable.


> 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.

Well, you do, if you implement exception safe RAII, which you 
should. RAII ensures that it will be released when the owner 
object is destructed.


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

Well, if you have a back pointer that is part of the invariant 
for the type, then neither move or copy work as expected. In C++ 
you have the address of the source object and can either modify 
or change the associated data-structure that provide back 
pointers (e.g. a global hash with pointers to the struct, in C++ 
you can change these pointers to point to the new location/add 
another entry). AFAIK this is not possible in D without adding an 
indirection.




More information about the Digitalmars-d mailing list