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