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

Lars T. Kyllingstad via Digitalmars-d digitalmars-d at puremagic.com
Sun Feb 14 04:53:08 PST 2016


On Saturday, 13 February 2016 at 22:42:34 UTC, Ola Fosheim 
Grøstad wrote:
> On Saturday, 13 February 2016 at 21:41:06 UTC, Lars T. 
> Kyllingstad wrote:
>> 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 know you said afterwards you didn't need a reference, but I'll 
give you one anyway. :)  That is the formal requirement for C++ 
standard library types; see sec. 17.6.5.15 [lib.types.movedfrom] 
of the C++ specification.

But I agree that, for the most part, one would expect that the 
moved-from object holds *no* resource and that the resource 
previously held by the target object has been released.


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

Pretty sure, but off the top of my head I can't give you too many 
concrete examples beyond the Meyers article I linked to, Stack 
Overflow questions, and one particular well-known and respected 
library (ZeroMQ) where I recently ran into it.


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

I was, yes.


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

For your lowest-level resource owners, I guess that is the case.  
But if a and b are largeish compound types that don't fit in a 
register, that's not the case, right?  Or can the optimiser deal 
with this in a good way too?


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

So what you're saying that a particular kind of designs/patters 
can not be safely combined with D's standard move mechanism.  
That is of course very unfortunate, but I guess it can be worked 
around?  I thought you were implying that simply using move() on 
any struct could potentially mess it up...


More information about the Digitalmars-d mailing list