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