January 21, 2019
On Monday, 21 January 2019 at 22:48:41 UTC, Jonathan M Davis wrote:
>
> A classic case would be some uses of RAII such as what MFC does with its hourglass. You get code something like
>
> Hourglass hg;
>
> and that does everything. The constructor pops up the hourglass, and the destructor takes it down. If you wanted an object like that in D, you would have to use a factory function. e.g.
>
> auto hg = Hourglass.create();
>
> and you would then have to make it so that the object can deal with the fact that the init value never when through the factory function and thus must do nothing in its destructor (using @disable this() to disable default initialization reduces that problem, but since the init value can still be used explicitly, it really doesn't eliminate the problem).
>
> Similarly, as Meta alludes to in his example, having dynamic arrays of mutable elements as member variables is problematic with structs, because all of the instances of that struct on the same thread have a dynamic array that is a slice of the same chunk of memory, meaning that mutating an element in one mutates all of them (at least all of them which haven't ended up with their array being reallocated due to appending or whatnot).

Ah!!

> In most cases, you'd really want a separate dynamic array for each instance of the struct, and D doesn't give a good way to do that with init values, forcing you to use a factory function instead of a constructor if you want to try and force it. And now that we can actually directly initialize member variables which are pointers or class references, those join the ranks of potentially having problems due to all of the instances of that struct on a particular thread having the same value for those pointers or references. So, it can become a bit error-prone for those types of member variables.
>
> And really, any and all situations where you're looking to have a piece of code run when an instance of the struct is created but where you don't necessarily have arguments for the constructor is going to be harder to deal with cleanly in D thanks to the lack of default constructors.
>
> In general, you just learn to live with it and use factory functions to deal with the problem in those cases where it pops up. So, it usually isn't a big deal, and it's mostly just something that folks new to D complain about, but having init values such that we cannot have default constructors for structs is an area in D that's a tradeoff, not a complete win. Many of us think that the tradeoffs involved are well worth it, but that doesn't change the fact that there are times when having a default constructor would be really nice, and the fact that you can't is then annoying.

Ok... see it now...

January 22, 2019
On Monday, 21 January 2019 at 20:50:45 UTC, Meta wrote:
> On Monday, 21 January 2019 at 19:08:49 UTC, Alex wrote:
>> Could you give an example, where a zero argument construction has to be done, which cannot be accomplished by setting the appropriate field with a default value?
>
> For the following reason, although the default argument constructor hack no longer works:
>
> import std.stdio;
>
> struct Test(bool useFieldDefaultVal)
> {
>     static if (useFieldDefaultVal)
>     {
>         int[] arr = [1, 2, 3];
>     }
>     else
>     {
>         int[] arr;
>
>         this(int dummy = 0)
>         {
>             arr = [1, 2, 3];
>         }
>     }
>
>     void doTest()
>     {
>     	writeln("Address of arr: ", arr.ptr);
>     }
> }
>
> void main()
> {
>     Test!true t1;
>     Test!true t2;
>
>     // Deprecation: constructor `onlineapp.Test!false.Test.this`
>     // all parameters have default arguments, but structs cannot
>     // have default constructors.
>     auto t3 = Test!false();
>
>     t1.doTest(); // Prints "Address of arr: 5622DAA0F010"
>     t2.doTest(); // Prints "Address of arr: 5622DAA0F010"
>     t3.doTest(); // Prints "Address of arr: null"
> }

Ok, so this confused me a bit, I seem to remember that when you static inisialize a class in the declaration scope of a struct, the same thing would happen as with arrays, i.e. they'd have the same address. But this turned out different:

import std.stdio;

class C {}

struct Test(bool useFieldDefaultVal)
{
    static if (useFieldDefaultVal)
    {
        C c = new C;
    }
    else
    {
        C c;

        this(int dummy = 0)
        {
            c = new C;
        }
    }

    void doTest()
    {
    	writeln("Address of c: ", &c);
    }
}

void main()
{
    Test!true t1;
    Test!true t2;

    // Deprecation: constructor `onlineapp.Test!false.Test.this`
    // all parameters have default arguments, but structs cannot
    // have default constructors.
    auto t3 = Test!false();

    t1.doTest(); // Prints "Address of arr: 7FFE1322A7E8"
    t2.doTest(); // Prints "Address of arr: 7FFE1322A7F0"
    t3.doTest(); // Prints "Address of arr: 7FFE1322A7F8"
}

t1 and t2 have different addresses and t3 has t1's address?? Huh?

Vat Khappened Khere?
January 22, 2019
On Saturday, 19 January 2019 at 11:13:22 UTC, Jonathan M Davis wrote:
> On Saturday, January 19, 2019 3:05:22 AM MST Dru via Digitalmars-d wrote:
>> What is the idea behind why we can't define default ctor for structs?
>>
>> In current situation I need to "@disable this()"
>> then define a constructor with dummy arguments  (because my
>> constructor does not need arguments)
>>
>> It is a big pain on syntax
>
> Structs in D don't actually have default constructors. Rather, they have a default value that they're initialized to - namely T.init (where T is the type name). In fact, all types in D have a default value so that they're guaranteed to not be garbage if you forget to initialize them with a specific value. This gets taken advantage of in a number of places. One obvious one is arrays. The init value can simply be blitted into all of the elements of the array. A number of features in D are built around that and really don't work without it.


I've always wondered why T.init can't be it's own thing and separate from this(). So if someone defined a this() then D just treats it like a custom non-default constructor? Not possible?
January 21, 2019
On Monday, January 21, 2019 6:23:21 PM MST aliak via Digitalmars-d wrote:
> On Saturday, 19 January 2019 at 11:13:22 UTC, Jonathan M Davis
>
> wrote:
> > On Saturday, January 19, 2019 3:05:22 AM MST Dru via
> >
> > Digitalmars-d wrote:
> >> What is the idea behind why we can't define default ctor for structs?
> >>
> >> In current situation I need to "@disable this()"
> >> then define a constructor with dummy arguments  (because my
> >> constructor does not need arguments)
> >>
> >> It is a big pain on syntax
> >
> > Structs in D don't actually have default constructors. Rather, they have a default value that they're initialized to - namely T.init (where T is the type name). In fact, all types in D have a default value so that they're guaranteed to not be garbage if you forget to initialize them with a specific value. This gets taken advantage of in a number of places. One obvious one is arrays. The init value can simply be blitted into all of the elements of the array. A number of features in D are built around that and really don't work without it.
>
> I've always wondered why T.init can't be it's own thing and
> separate from this(). So if someone defined a this() then D just
> treats it like a custom non-default constructor? Not possible?

If D allowed struct's to have a constructor without any parameters, it would not be and could not be a default constructor because of how init works. It would only be a constructor that was called when used explicitly. As such, it wouldn't really add much over just using a factory function, and it would run a serious risk of confusing people, because newcomers would expect this() to be a default constructor when it wasn't. It also would be bad for porting code to D from languages like C++, because without extra work from the programmer, the code would assume that this() was a default constructor when it wasn't, making it easy to end up with subtle bugs. By outright making this() illegal, D forces the programmer to deal with the situation rather than allowing silent bugs. If it hadn't, I can guarantee that there would have been tons of confusion and complaints over how this() wasn't working correctly, because everyone coming to D would expect it to be a default constructor and then be very confused when it wasn't being called in code like

Foo foo;

or

auto arr = new Foo[](42);

The only downside that I can think of at the moment for using a factory function over having this() as a non-default constructor is that when constructing immutable objects, the constructor usually has to do it (though in some situations, casts could be used - that depends primarily on whether the data is guaranteed to be unique). So, such a factory function would require a special constructor with dummy arguments or something similar in order to construct immutable objects. But aside from that, a factory function is just as good as a constructor, and it doesn't carry with it the confusion of whether this() is a default constructor or not.

- Jonathan M Davis



January 22, 2019
On Tuesday, 22 January 2019 at 01:18:24 UTC, aliak wrote:
> On Monday, 21 January 2019 at 20:50:45 UTC, Meta wrote:
>> On Monday, 21 January 2019 at 19:08:49 UTC, Alex wrote:
>>> Could you give an example, where a zero argument construction has to be done, which cannot be accomplished by setting the appropriate field with a default value?
>>
>> For the following reason, although the default argument constructor hack no longer works:
>>
>> import std.stdio;
>>
>> struct Test(bool useFieldDefaultVal)
>> {
>>     static if (useFieldDefaultVal)
>>     {
>>         int[] arr = [1, 2, 3];
>>     }
>>     else
>>     {
>>         int[] arr;
>>
>>         this(int dummy = 0)
>>         {
>>             arr = [1, 2, 3];
>>         }
>>     }
>>
>>     void doTest()
>>     {
>>     	writeln("Address of arr: ", arr.ptr);
>>     }
>> }
>>
>> void main()
>> {
>>     Test!true t1;
>>     Test!true t2;
>>
>>     // Deprecation: constructor `onlineapp.Test!false.Test.this`
>>     // all parameters have default arguments, but structs cannot
>>     // have default constructors.
>>     auto t3 = Test!false();
>>
>>     t1.doTest(); // Prints "Address of arr: 5622DAA0F010"
>>     t2.doTest(); // Prints "Address of arr: 5622DAA0F010"
>>     t3.doTest(); // Prints "Address of arr: null"
>> }
>
> Ok, so this confused me a bit, I seem to remember that when you static inisialize a class in the declaration scope of a struct, the same thing would happen as with arrays, i.e. they'd have the same address. But this turned out different:
>
> import std.stdio;
>
> class C {}
>
> struct Test(bool useFieldDefaultVal)
> {
>     static if (useFieldDefaultVal)
>     {
>         C c = new C;
>     }
>     else
>     {
>         C c;
>
>         this(int dummy = 0)
>         {
>             c = new C;
>         }
>     }
>
>     void doTest()
>     {
>     	writeln("Address of c: ", &c);
>     }
> }
>
> void main()
> {
>     Test!true t1;
>     Test!true t2;
>
>     // Deprecation: constructor `onlineapp.Test!false.Test.this`
>     // all parameters have default arguments, but structs cannot
>     // have default constructors.
>     auto t3 = Test!false();
>
>     t1.doTest(); // Prints "Address of arr: 7FFE1322A7E8"
>     t2.doTest(); // Prints "Address of arr: 7FFE1322A7F0"
>     t3.doTest(); // Prints "Address of arr: 7FFE1322A7F8"
> }
>
> t1 and t2 have different addresses and t3 has t1's address?? Huh?
>
> Vat Khappened Khere?

You're printing the address of the object reference, not the object itself (similar to printing the address of a pointer instead of the actual address it points to). Do `cast(void*)c` instead.
January 22, 2019
On Tue, 22 Jan 2019 01:18:24 +0000, aliak wrote:
>      t1.doTest(); // Prints "Address of arr: 7FFE1322A7E8"
>      t2.doTest(); // Prints "Address of arr: 7FFE1322A7F0"
>      t3.doTest(); // Prints "Address of arr: 7FFE1322A7F8"
> }
> 
> t1 and t2 have different addresses and t3 has t1's address?? Huh?\

E8 != F8.
January 22, 2019
On Tuesday, 22 January 2019 at 03:36:31 UTC, Neia Neutuladh wrote:
> On Tue, 22 Jan 2019 01:18:24 +0000, aliak wrote:
>>      t1.doTest(); // Prints "Address of arr: 7FFE1322A7E8"
>>      t2.doTest(); // Prints "Address of arr: 7FFE1322A7F0"
>>      t3.doTest(); // Prints "Address of arr: 7FFE1322A7F8"
>> }
>> 
>> t1 and t2 have different addresses and t3 has t1's address?? Huh?\
>
> E8 != F8.

lol. True.
January 22, 2019
On Tuesday, 22 January 2019 at 03:30:34 UTC, Meta wrote:
>
> You're printing the address of the object reference, not the object itself (similar to printing the address of a pointer instead of the actual address it points to). Do `cast(void*)c` instead.

Doh! Thanks.

January 22, 2019
> If D allowed struct's to have a constructor without any parameters, it would not be and could not be a default constructor because of how init works. It would only be a constructor that was called when used explicitly.

the problem is construction of static variables right?
we could allow to define a default ctor and then give an error in case it is used for a static variable.

A a; //would error if A has a runtime default ctor

these would still work:
A a = A.init;
A a = void;


January 23, 2019
On Tuesday, January 22, 2019 2:38:41 PM MST Dru via Digitalmars-d wrote:
> > If D allowed struct's to have a constructor without any parameters, it would not be and could not be a default constructor because of how init works. It would only be a constructor that was called when used explicitly.
>
> the problem is construction of static variables right?
> we could allow to define a default ctor and then give an error in
> case it is used for a static variable.
>
> A a; //would error if A has a runtime default ctor
>
> these would still work:
> A a = A.init;
> A a = void;

It's far from just static variables. For instance, init is a core part of how arrays are initialized. You can't even put something in an array if default initialization is disabled for that type. The language in general assumes that everything is default-initialized, and when it can't be, you start running running into stray places where you can't do stuff that you can normally do. Introducing any kind of default construction to structs would be a massive shift, and I doubt that I could come up with all of the stuff that would be affected off the top of my head.

- Jonathan M Davis