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