About two month ago, I started a thread about the possibility of having a "no-arg" constructor (not to be confused with a "default" constructor). The thread was: http://forum.dlang.org/thread/icjwbtlxsaekksyol...@forum.dlang.org

Back then, the language was still new to me (I have a better grip on it now), and I got extra information during the thread, which threw me off course. One of the better arguments throw at me from another thread (by Andrei), was that a no-arg constructor with interfere with the "auto a = S();" syntax. I had no rebuke at the time.

I'd like to restart this conversation. First, by showing a no-arg constructor is needed, and then, by showing how we should be able to plug it into the language.

****************************
The biggest issue with not having a no-arg constructor can easilly be seen if you have ever worked with a "Reference Semantic" semantic struct: A struct that has a pointer to a payload. Basically, a class, but without the inherited Object polymorphism. These are hard to work with, both for the user and the implementer: They either use auto-intialization, making EVERY CALL start with "ensure initialied" (costly for ranges). Either that, or they need to be explicitly initialized. Or a mix of both, and a source of bugs and frustration in phobos.

Anyways, let's start with an example. For the sake of simplicity, I defined two structs to avoid the "structs with a constructor can't be default newed" bug;
----
struct S
{
    int* p;
}
struct S2
{
    int* p;
    this(int){};
}

void main()
{
    S a;
    S* pa;
    //auto b  = S;
    auto pb = new S;
    auto c  = S.init;
    //auto pc = ???
    auto d  = S();
    auto pd = new S();
    auto e  = S2(5);
    auto pe = new S2(5);
}
----
In the above code, a-c/pa-pc are not initialized, as expected.
e/pe are initialized, as expected.

HOWEVER, and in contrast to classes, it is surprising that "auto d = S();" and "auto pd = new S();" does not create an initialized reference semantic struct. It is a bare minimum to give a user the ability to allocate & initialize in a single call...

This is a problem for both user *and* implementer: The user will have trouble initializing his struct, whereas the implementer will have trouble on his end properlly implementing his struct.

I was trying to develop one such struct. One solution was to use the opCall "hack". Andrei suggested I migrate to a class. On one end, I think his suggestion is the better approach here. On the other hand I also think that if a developer is forced into migrating from a struct to a class because of implementation detail, or using a hack, it is a tell-tale sign of something stinky.

****************************
What is very interesting to note above (IMO), is that the language provides no less than THREE syntaxes to allocate a non-constructed S, two of which can be used with auto:
*Explicit typing (a)
*For stack: S.init (c),               parenthesis (d)
*for new:   without parenthesis (pb), with parenthesis (pd)

If we have such extra ways, then surely, one of the two can be used to call the no arg constructor, while the other is just an S.init memcopy, right? Here is my proposal:

----
struct S
{
    int* p;
    this(){};    //no arg constructor
    this(int){}; //arg constructor
}

void main()
{
    S a;                //Not initialized
    S* pa;              //Not initialized

    auto b1  = S;       //Not initialized (new semantic)
    auto b2  = S.init;  //Not initialized (verbose semantic)
    auto pb = new S;    //Not initialized

    auto e  = S2(5);     //Initialized, calls this(int)
    auto pe = new S2(5); //Initialized, calls this(int)

auto d = S(); //Initialized, calls this() (migrating semantic) auto pd = new S(); //Initialized, calls this() (migrating semantic)
}
----
As is shown in this example, the language semantics should be perfectly capable of handling this.

The "issues" we may encounter are more due to the ambiguities with the "old" semantics: *Regarding the "migrating semantic": This form is currently available in D. I propose it remains useable for now, but later becomes deprecated if the struct does not have a no-arg constructor.

*Regarding "b1": I propose this semantic become legal. It is legal with "auto pb = bew S;", so "auto b = S;" should also be accepted. The alternative would be to use "S.init", but this is more verbose, and more "explicit".

************
I realize this would be a big change to the *core* language, yet the no no-arg constructor has felt like a (breaking) limitation from day one, and it would be really nice if we could support it.

I realize *why* the "default" constructor had to go, but "no-arg" didn't have to go with it. I think it was an accident to let it go, and we should be trying to fix this.

Could I get some feedback so I could make a formal and thorough enhancement request?

Reply via email to