www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Field Initialiser Reused Across Object Instances

reply Adam <thewalker_adam yahoo.co.uk> writes:
Consider the following:

class A
{
     int x;
}

class B
{
     A a = new A; // I would expect this to be called for each 
"new B".
}

void main()
{
     import std.stdio;

     auto c = new B;
     writeln("c ", c.a.x);
     c.a.x++;
     writeln("c ", c.a.x);
     writeln;

     auto d = new B;
     writeln("d ", d.a.x, " expected 0!");
     d.a.x++;
     writeln("d ", d.a.x, " expected 1!");
}

*** Output ***
c 0
c 1

d 1 expected 0!
d 2 expected 1!

There is only one instance of A in the above program, although I 
would expect one for each instance of B.  The field is not marked 
static.

Is this intended?  My gut reaction is the compiler is memoising 
"new A" because purity is inferred, but doesn't that contradict 
the meaning of "new Class" which should always yield an object 
with it's own identity?

I can fix using a dedicated constructor, but I much prefer 
initializers where possible.
Jan 01
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Friday, 1 January 2021 at 13:34:16 UTC, Adam wrote:
     A a = new A; // I would expect this to be called for each 
 "new B".
Your expectation is wrong for D. Since that's in a `static` context, it is run at compile time. Any variable marked `static`, `__gshared`, or is struct/class/module-top-level member is a static context. So all those are set up at compile time (or triggers a compile error if this fails btw, either a thing is a static context and thus CTFEd or not a static context and thus never CTFEd) and just blindly copied at run time. Basically any assignment outside a function is a compile time thing.
 Is this intended?
Yes, it is a frequent thing to surprise people though. The reference there is part of the object but it points to the same default thing because of how that was made. Constructors are actually run at runtime, but initializers are considered compile time constants.
  My gut reaction is the compiler is memoising "new A" because 
 purity is inferred
note that ctfe never happens because of anything inferred or pure or stuff like that. It is purely decided by the initialization context.
 I can fix using a dedicated constructor, but I much prefer 
 initializers where possible.
no real choice here, if you want a unique object to be referenced it needs to run with the constructor.
Jan 01
parent Adam <thewalker_adam yahoo.co.uk> writes:
That's a fantastic answer!  Thank you.  I was not aware that 
initializers were always compile time, that was the missing piece 
in my understanding.

It's a shame that I can't use the nicer (IMO) syntax, but the 
reasoning is sound.
Jan 01