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