On Saturday, 13 February 2016 at 17:47:54 UTC, Lars T.
Kyllingstad wrote:
Not knowing anything about the libraries you write, it's hard
to argue with that. But I agree that given that you are in
control of all the code AND can make the move ctor/assignment
available for inlining (AND are an experienced programmer),
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. (Of course, parts of the C++
standard library is opinionated.)
cannot be inlined. Then, you are looking at multiple levels of
function calls and you are also at the mercy of whoever wrote
their move code.
Well, I primarily use move semantics for ownership, like owning
resources in the GPU, files system, memory etc. So it usually is
1 or 2 levels.
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...
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...
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?
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.
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).
So, C++ gives the library author control by having "move" be part
of the type system that essentially does nothing else than
applying some constraints. Having "move" as an action is both
limiting and potentially flawed, since the D compiler does not do
anything to ensure correctness. If "move" is an action, rather
than a type system constraint, then it should be backed up with
semantic analysis IMO.