On 02/16/2016 05:50 PM, Matt Elkins wrote:
On Tuesday, 16 February 2016 at 08:18:51 UTC, Ali Çehreli wrote:
When a temporary Foo object is moved into the array, the temporary
object is set to Foo.init. This temporary object lives on the stack.
In fact, all temporary Foo objects of Foo.this(int) live at the same
location.

After Foo(8) is moved into the array and set to Foo.init, now Foo(1)
is constructed on top of it. For that to happen, first the destructor
is executed for the first life of the temporary, and so on...

There is one less Foo.init destruction because conceptually the
initial temporary was not constructed on top of an existing Foo.init
but raw memory.

I guess that makes sense. But doesn't it imply that a copy is happening,
despite the @disabled post-blit? The desired behavior was to construct
the Foos in place, like with a C++ initializer list.


I changed my mind! :) This seems to be how D is being clever in constructors. I've just learned something (thank you!), which may very well be a bug rather than a feature.

The compiler analyzes the body of the constructor to differentiate between first use versus first initialization. For example, unlike C++, you can call super() anywhere in the constructor.

So, although this(this) is disabled, moving objects into a static-array member in a constructor must be allowed because otherwise we could not initialize such members. Since a static array must consist of .init values to begin with, every move into its members must also trigger its destructor if the type has elaborate destructor.

In this example, the compiler is being smart and skips one of the "move+destructor" operations. I felt this behavior in my other post where I had alluded to "raw memory".

This is what I've discovered: Try printing the static array at the beginning of the constructor:

    this(int)
    {
        writefln("Initial foos:");
        foreach (ref f; foos[]) {
            writeln(" ", f.value);
        }
        writeln("starting to assign");

        // ...
    }

Error:  field 'foos' initialization is not allowed in loops or after labels

So, this tells us (in a cryptic way) that the syntax foos[] at that point in the constructor is understood as "initialization" not as slicing. To contrast, now move that code below the Foo(8) assignment:


struct Foo
{
    // ...
    int value = 7;    // <-- Do this as well
}

    this(int)
    {
        writeln("Before 8");
        foos[0] = Foo(8);         // <-- FIRST ASSIGNMENT

        writefln("Initial foos:");
        foreach (ref f; foos[]) {
            writeln(" ", f.value);
        }
        writeln("starting to assign");

        // ...
    }

Now the code compiles and prints the contents of the array *without* any Foo destructor. (I think this is because Foo(8) is emplaced, rather than assigned.)

Before 8
Initial foos:
 8
 7
 7
 7
 7
starting to assign
Before 1
[...]

Only after that point we have an array of valid Foos where further assignments trigger destructors.

Ali

Reply via email to