no-arg constructor for structs (again)

monarch_dodra monarchdodra at gmail.com
Wed Sep 19 04:52:10 PDT 2012


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/icjwbtlxsaekksyoljfp@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?


More information about the Digitalmars-d mailing list