Jump to page: 1 2 3
Thread overview
WTF! new in class is static?!?!
Jun 07, 2018
DigitalDesigns
Jun 07, 2018
Ethan
Jun 07, 2018
ag0aep6g
Jun 07, 2018
Jonathan M Davis
Jun 07, 2018
aliak
Jun 08, 2018
Jonathan M Davis
Jun 09, 2018
aliak00
Jun 07, 2018
Stefan Koch
Jun 07, 2018
Cym13
Jun 07, 2018
Adam D. Ruppe
Jun 07, 2018
DigitalDesigns
Jun 07, 2018
DigitalDesigns
Jun 09, 2018
bauss
Jun 09, 2018
RealProgrammer
Jun 09, 2018
rikki cattermole
Jun 09, 2018
KingJoffrey
Jun 09, 2018
12345swordy
Jun 10, 2018
bauss
Jun 10, 2018
KingJoffrey
Jun 10, 2018
Bauss
June 07, 2018
class A;

class B
{
   A a = new A();
}

auto b1 = new B();
auto b2 = new B();

assert(b1.a == b2.a)!!


I'm glad I finally found this out! This is not typical behavior in most languages is it?

I'd expect it to be translated to something like

class B
{
   A a;
   this()
   {
       a = new A();
   }
}

In C# it is different, can't remember if it is different in C++. This has caused bugs in my code because the fields are all pointing to the same data when I expected them to each have unique data ;/

This method is error prone and the behavior should be reversed, it should not break the majority of code. If one wants the current behavior then static new could be used or something else.


June 07, 2018
On Thursday, 7 June 2018 at 21:07:26 UTC, DigitalDesigns wrote:
> assert(b1.a == b2.a)!!

The spec isn't clear on this but it uses the same rules as struct field initialisation, ie it's defined once and copied to each instance on creation.

https://dlang.org/spec/struct.html#default_struct_init
June 07, 2018
On Thursday, June 07, 2018 21:07:26 DigitalDesigns via Digitalmars-d-learn wrote:
> class A;
>
> class B
> {
>     A a = new A();
> }
>
> auto b1 = new B();
> auto b2 = new B();
>
> assert(b1.a == b2.a)!!
>
>
> I'm glad I finally found this out! This is not typical behavior in most languages is it?
>
> I'd expect it to be translated to something like
>
> class B
> {
>     A a;
>     this()
>     {
>         a = new A();
>     }
> }
>
> In C# it is different, can't remember if it is different in C++. This has caused bugs in my code because the fields are all pointing to the same data when I expected them to each have unique data ;/
>
> This method is error prone and the behavior should be reversed, it should not break the majority of code. If one wants the current behavior then static new could be used or something else.

Well, if that compiles now with a non-immutable class object, then that was a language improvement.

In any case, yes, the class object would be shared across all instances of the class.

_Every_ type in D has an init value that they get default-initialized to before the constructor is run (assuming that the type even has a constructor). In the case of both classes and structs, whatever the member variables are directly initialized with make up the init value. So, if you have something like

struct S
{
    int i = 42;
}

then every instance of S will start with the value of 42 for i, and if you have something like

struct S
{
    int i = 42;

    this(int j)
    {
        i = j;
    }
}

then S.i is 42 before the constructor is run and will then be whatever it gets assigned to in the constructor after the constructor has run.

The situation with classes is exactly the same as structs except that you don't have direct access to the init value (since you only ever deal with class references, not the classes themselves). One key result of this is that the class object is fully initialized to its init value for its exact type before _any_ constructors are called, so you don't get that problem that C++ has where the object isn't fully its correct type until all of the constructors have been called (so calling virtual functions from a class constructor in D actually works, unlike C++).

All of this is quite clean with value types, but in the case of member variables that are pointers, dynamic arrays, or reference types that would mean that if you had something like

struct S
{
    int* ptr = new int(42);
}

every instance of S would have the same exact value for S.ptr, which can be surprising and is why it's sometimes been suggested that it not be legal to directly initialize member variables with mutable, non-value types. However, historically, this really only mattered for dynamic arrays, because originally it wasn't legal to directly initialize any member variable that was a pointer or reference, because the compiler and runtime weren't sophisticated enough to handle it. Several years ago, it was made possible to directly initialize immutable class references, but that doesn't really cause any problems, since sharing an immutable object doesn't cause problems. However, if it's now possible for the init value of an object to contain a mutable class reference or pointer, then the problem does get worse, and arguably, it becomes more critical to just make it illegal in the case of member variables in order to avoid the surprises (and bugs) that come when folks misunderstand what it really means to directly initialize a member variable in D.

In any case, the way that D works here is a direct result of how init values work, and it really doesn't make sense for it to work any other way. At most, it would make sense to simply make it illegal to directly initialize types where it would be a problem.

- Jonathan M Davis

June 07, 2018
On Thursday, 7 June 2018 at 21:07:26 UTC, DigitalDesigns wrote:
> class A;
>
> class B
> {
>    A a = new A();
> }
>
> auto b1 = new B();
> auto b2 = new B();
>
> assert(b1.a == b2.a)!!
>
>
> I'm glad I finally found this out! This is not typical behavior in most languages is it?
>
> I'd expect it to be translated to something like
>
> class B
> {
>    A a;
>    this()
>    {
>        a = new A();
>    }
> }
>
> In C# it is different, can't remember if it is different in C++. This has caused bugs in my code because the fields are all pointing to the same data when I expected them to each have unique data ;/
>
> This method is error prone and the behavior should be reversed, it should not break the majority of code. If one wants the current behavior then static new could be used or something else.

If you want a new one use a constructor call.
initalizers are support to be static, this only works because ctfe supports newing classes.

June 07, 2018
On Thursday, 7 June 2018 at 21:07:26 UTC, DigitalDesigns wrote:
> class A;
>
> class B
> {
>    A a = new A();
> }
>
> auto b1 = new B();
> auto b2 = new B();
>
> assert(b1.a == b2.a)!!
>
>
> I'm glad I finally found this out! This is not typical behavior in most languages is it?
>
> I'd expect it to be translated to something like
>
> class B
> {
>    A a;
>    this()
>    {
>        a = new A();
>    }
> }
>
> In C# it is different, can't remember if it is different in C++. This has caused bugs in my code because the fields are all pointing to the same data when I expected them to each have unique data ;/
>
> This method is error prone and the behavior should be reversed, it should not break the majority of code. If one wants the current behavior then static new could be used or something else.

The spec looks pretty clear to me on that point https://dlang.org/spec/class.html#field-init

Besides, defining behaviour at construction is what constructors are for, I wouldn't expect anything outside a constructor to happen when an object is constructed. So while I understand that other languages may (successfully for them) do things differently I don't think I'd like a breaking change for that.
June 07, 2018
On Thursday, 7 June 2018 at 21:07:26 UTC, DigitalDesigns wrote:
> I'm glad I finally found this out! This is not typical behavior in most languages is it?


I don't think most languages allow this, and D used to not allow it either, but then CTFE got class support and it got enabled. If you understand how it works, it is sometimes useful...

But, it is confusing for a *lot* of people so it might make sense to ban it again - or at least make it more obvious that this is what you intended when you write it.
June 07, 2018
On 6/7/18 5:07 PM, DigitalDesigns wrote:
> class A;
> 
> class B
> {
>     A a = new A();
> }
> 
> auto b1 = new B();
> auto b2 = new B();
> 
> assert(b1.a == b2.a)!!

Yep, long-standing issue: https://issues.dlang.org/show_bug.cgi?id=2947

Almost a decade old!

-Steve
June 08, 2018
On 06/07/2018 11:26 PM, Ethan wrote:
> The spec isn't clear on this but it uses the same rules as struct field initialisation, ie it's defined once and copied to each instance on creation.
> 
> https://dlang.org/spec/struct.html#default_struct_init

It says there that "The default initializers may not contain references to mutable data."

DMD enforces that for pointers to primitives, but not for arrays, class objects, or even pointers to structs:

----
struct S
{
    int* p = new int(1); /* Error: cannot use non-constant CTFE pointer in an initializer */
    int[] a = [1, 2, 3]; /* no error */
    C c = new C; /* no error */
    S2* s = new S2; /* no error */
}

class C { int x = 4; }
struct S2 { int x = 5; }
----

Seems inconsistent to me.
June 07, 2018
On Thursday, 7 June 2018 at 21:32:54 UTC, Jonathan M Davis wrote:

> struct S
> {
>     int* ptr = new int(42);
> }
>

Is that supposed to compile? -> https://run.dlang.io/is/SjUEOu

Error: cannot use non-constant CTFE pointer in an initializer &[42][0]
June 07, 2018
On Thursday, 7 June 2018 at 21:57:17 UTC, Steven Schveighoffer wrote:
> On 6/7/18 5:07 PM, DigitalDesigns wrote:
>> class A;
>> 
>> class B
>> {
>>     A a = new A();
>> }
>> 
>> auto b1 = new B();
>> auto b2 = new B();
>> 
>> assert(b1.a == b2.a)!!
>
> Yep, long-standing issue: https://issues.dlang.org/show_bug.cgi?id=2947
>
> Almost a decade old!
>
> -Steve

wait! everyone is saying it is a feature! So, which is it, a feature or a bug?!?!?
« First   ‹ Prev
1 2 3