Is "auto t=T();" not the same as "T t;"?

H. S. Teoh hsteoh at qfbox.info
Wed Oct 26 00:44:45 UTC 2022


On Wed, Oct 26, 2022 at 12:16:55AM +0000, Salih Dincer via Digitalmars-d-learn wrote:
[...]
> I've known D for more than 10 years, but the topic we're talking about
> still seems strange to me.  The explanations given are not enough for
> me, I'm sorry.
> 
> Can anyone tell me what happens when I change the location of the
> structure?  So the X structure must be in the stack when it is in
> main(), and the following must be in the heap, right?
> 
> ```d
> import std;
> 
> //void main() {
> 
>   struct X
>   {
>     struct Y {
>       int i = 10;
>       alias i this;
>     }
>     Y[] y = [Y.init];

This declares y to be a slice of some memory somewhere (it can be either
the stack or the heap), and initializes it to point to a 1-element array
containing Y.init.

Here's an important quirk in D: whenever you initialize an aggregate
array member with a literal, ALL instances of the aggregate will point
to the *same* underlying array (see below).

If you want to avoid the confusing situation below, my recommendation is
that you initialize .y inside the ctor instead. Then it will be more
explicit what exactly is going on.


>     string toString() {
>       return y.format!"%s";
>     }
>   }
> 
> void main() {
> 
>   X[2] x;

This declares a static array of 2 elements, each of which is an X. Each
instance of X contains a member y that is a slice that points to the
array [Y.init].  Each instance of X sits on the stack; however, their
members y point somewhere else, in this case, to the array [Y.init].

And here's the important point: as mentioned above, [Y.init] here is the
SAME ARRAY that's being referred to by two different slices: x[0].y and
x[1].y.

If you'll excuse some ASCII art, here's the situation you have:

	STACK                  GLOBAL DATA
	x[0] {
	    Y[] y; -----+----> [ Y.init ]
	}               |
	x[1] {          |
	    Y[] y; -----'
	}


>   x.writeln;       // [[10], [10]]
> 
>   x[0].y[0] = 0;   // [[0], [0]]

This line in essence says, "assign 0 to the first element of the array
in the slice y, in the first element of array x".  Since both x[0].y and
x[1].y point to the same underlying array, modifying it via x[0].y will
also cause x[1].y to see the change.

IOW, the revised ASCII art diagram now looks like this:

	STACK                  GLOBAL DATA
	x[0] {
	    Y[] y; -----+----> [ 0 ]
	}               |
	x[1] {          |
	    Y[] y; -----'
	}

That is why x[0].y[0] == 0 and also x[1].y[0] == 0. This is because
x[0].y.ptr == x[1].y.ptr.


>   x.writeln;
> 
>   x[0].y.length++;

This line tries to increase the length of the array pointed to by
x[0].y. Since it was declared as a 1-element literal, which is allocated
in program global data area, there isn't room to expand it.  So at this
point, in order to honor the request to lengthen the array, druntime
will allocate a new array on the heap and copy the contents of the old
array over, then expand its length to 2.  The situation now looks like
this:

	STACK                  GLOBAL DATA           HEAP
	x[0] {
	    Y[] y; --------------------------------> [ 0, 0 ]
	}                
	x[1] {           
	    Y[] y; ----------> [ 0 ]
	}

Key point: x[0].y and x[1].y now point to two different arrays, in two
different places. Changes in one will no longer reflect in the other.

The array [ 0 ] shown above is the same array that x[0].y *used* to
point to, but no longer does because druntime has made a copy of it in
the heap and updated x[0].y to point to the copy instead of the
original.  x[1].y, however, continues to point to the original array.


>   x[0].y[1] = 1;

This modifies the second element of x[0].y to 1. Situation now looks
like this:

	STACK                  GLOBAL DATA           HEAP
	x[0] {
	    Y[] y; --------------------------------> [ 0, 1 ]
	}                
	x[1] {           
	    Y[] y; ----------> [ 0 ]
	}


>   x[1].y[0] = 2;

This modifies the first element of x[1].y to 2. Situation:

	STACK                  GLOBAL DATA           HEAP
	x[0] {
	    Y[] y; --------------------------------> [ 0, 1 ]
	}                
	x[1] {           
	    Y[] y; ----------> [ 2 ]
	}


>   x.writeln;       // [[0, 1], [2]]

Which reflects the situation shown above.


T

-- 
Why ask rhetorical questions? -- JC


More information about the Digitalmars-d-learn mailing list