Discussion Thread: DIP 1040--Copying, Moving, and Forwarding--Community Review Round 1
deadalnix
deadalnix at gmail.com
Tue Mar 16 22:24:12 UTC 2021
On Monday, 15 March 2021 at 07:48:04 UTC, Walter Bright wrote:
> Both the move constructor and the destructor are in the same
> struct, and should be developed at the same time. At some
> point, the program has to rely on the programmer knowing what
> he's doing when doing storage management.
That is a notoriously difficult thing to get right, and why the
mechanism of constructor and destructor has been invented in the
first place. Consider for instance that destructor design also
destructs each field of the struct, in addition of running the
user's code. One could argue that the the developer could
destruct all fields manually, and in fact, it is certainly
possible to do so. But the design chosen doesn't do that because
we know ahead of time what would happen: leakage galore.
One way to think of it is in term of solidity. If I change
something at point A, will something else break unexpectedly in a
subtle way at point B? If yes, then the design is not solid.
When a new member is added to a struct, then this member will be
destroyed automatically too, just like the others. This is
correct by design. This is solid. And this is composable as the
destruction of each fields simply work and the code that has to
be written is limited to ensuring some cross fields invariants
remains true.
If one uses the exemple of a RC smart pointer for instance, the
smart pointer does not need to know how to destroy its payload,
the payload knows this. The RC simply keep the RC count up to
date and chooses to destroy or not its payload based on this, ie
it maintains the invariant between the state of the RC and the
state of the payload.
If no such invariant needs to be maintained, then the destructor
can remain empty and everything works fine.
Now what happens with move? Well,t he natural way to transpose
the described design to move is as follow:
1/ move all fields one by one
2/ call the move constructor on the result to maintain invariants
if need be.
To me, 2/ furiously sounds like a postblit, but I'm open to the
fact that alternatives. I however know for a fact that the
proposed magic will open a bag of worm because it doesn't go with
the same set of design principle as the rest of the
contruction/destruction business.
For instance, if a field were to be added to a struct, then
immediately the move assignment becomes invalid, silently. Worse,
if the field itself contains something detructible, now there is
something seriously wrong, potentially outside of the struct I'm
working with. For instance, if that new field is a smart pointer,
then the guarantee provided y the smart pointer are broken,
silently.
Now we might decide, instead, that all field are going to be
destroyed at the end of the move assign in the move struct, inc
are there are leftovers. But we are now back to the situation
where all struct MUST have a null state, or you won't be able to
have them as fields of other structs.
Or we break the guarantees provided by the ctor/dtor mechanism,
but in this case, why have it at all? The whole point of ctor and
dtor is to ensure that invariant are kept within the program.
Let's not break this invariant.
More information about the Digitalmars-d
mailing list