Arrays of noncopyables/Invalid memory operation

Matt Elkins via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Wed Feb 17 14:20:00 PST 2016


So in a different thread someone mentioned that when arrays are 
grown an implicit copy could be called on all the elements, as 
they might need to be copied over to a new, larger block of 
memory. This makes sense, and is expected. However, it got me 
concerned: what if the post-blit was disabled because it is 
illegal to copy the object? I am using a lot of objects exactly 
like this, and wanted to make sure my program wasn't working by 
coincidence since my dynamic arrays are still small for now. So I 
tested:

[code]
import std.stdio;
@safe:

bool scopeEnded;

struct Foo
{
     @disable this(this);

     this(int val) {writeln("Constructing: ", val, " (", &this, 
")"); value = val;}
     ~this() {writeln("Destroying: ", value, " (", &this, ")"); 
assert(value == int.init || scopeEnded);}
     int value;
}

unittest
{
     Foo[] foos;
     for (auto i = 0; i < 10000; ++i)
     {
         ++foos.length;
         foos[$ - 1] = Foo(i);
     }

     writeln("Scope about to end");
     scopeEnded = true;
}
[/code]

[output]
Constructing: 0 (18FDA8)
Destroying: 0 (18FD6C)
Constructing: 1 (18FDA8)
Destroying: 0 (18FD6C)
Constructing: 2 (18FDA8)
Destroying: 0 (18FD6C)

....<snipped>....

Constructing: 410 (18FDA8)
Destroying: 0 (18FD6C)
Constructing: 411 (18FDA8)
Destroying: 0 (18FD6C)
Constructing: 412 (18FDA8)
Destroying: 0 (18FD6C)
Constructing: 413 (18FDA8)
Destroying: 0 (18FD6C)
Constructing: 414 (18FDA8)
Destroying: 0 (18FD6C)
Constructing: 415 (18FDA8)

Destroying: 0 (18FD6C)
core.exception.InvalidMemoryOperationError at src\core\exception.d(679): Invalid memory operation
Constructing: 416 (18FDA8)
----------------
Destroying: 0 (18FD6C)

Constructing: 417 (18FDA8)
core.exception.InvalidMemoryOperationError at src\core\exception.d(679): Invalid memory operation
Destroying: 0 (18FD6C)
Constructing: 418 (18FDA8)
Destroying: 0 (18FD6C)
Constructing: 419 (18FDA8)
Destroying: 0 (18FD6C)
Constructing: 420 (18FDA8)
Destroying: 0 (18FD6C)
Constructing: 421 (18FDA8)
----------------
Destroying: 0 (18FD6C)
Constructing: 422 (18FDA8)
Destroying: 0 (18FD6C)
Constructing: 423 (18FDA8)

....<snipped>....

Constructing: 506 (18FDA8)
Destroying: 0 (18FD6C)
Constructing: 507 (18FDA8)
Destroying: 0 (18FD6C)
Constructing: 508 (18FDA8)
Destroying: 0 (18FD6C)
Constructing: 509 (18FDA8)
Destroying: 0 (18FD6C)
Destroying: 29 (5201F4)
Program exited with code 1
[/output]

Note that the invalid memory operation lines change relative 
order in this output, I think maybe it is stderr instead of 
stdout.

So now I'm wondering:
* Is the fact that this compiles a bug? If copying can happen 
under the hood, shouldn't the @disable this(this) prevent Foo 
being used this way? Or should copying be happening at all (the 
compiler could instead choose to "move" the Foo by blitting it 
and NOT running the destructor...though I don't know whether that 
is a safe choice in the general case)?
* What is the invalid memory operation? It doesn't occur if I 
remove the assert or make the assert never fail, so I'm guessing 
it has to do with failing an assert in the destructor? This 
points bothers me less than the previous one because I don't 
expect an assert-failing program to behave nicely anyway.


More information about the Digitalmars-d-learn mailing list