It stems from the fact that all types in D must have a default value. There are quite a few places where a type's init
value gets used, including stuff like default-initializing member variables and default-initializing every value in an array when it's allocated, and init
needs to be known at compile for a number of those cases. Having init
provides quite a few benefits, but it does get in the way of having a default constructor.
A true default constructor would need to be used in all of the places that init
is used (or it wouldn't be the default), but allowing arbitrary code to run in a number of the cases that init
is used would be problematic at best. At minimum, you'd probably be forced to make it CTFE-able and possibly pure
. And as soon as you start putting restrictions like that on it, pretty soon, you might as well just directly initialize all of the member variables to what you want (which is what happens with init
), as you wouldn't be gaining much (if anything) over that, which would make having default constructors pretty useless.
It might be possible to have both init
and a default constructor, but then the question comes up as to when one is used over the other, and the default constructor wouldn't really be the default anymore. Not to mention, it could become very confusing to developers as to when the init
value was used and when the default constructor was used.
Now, we do have the ability to @disable
the init
value of a struct (which causes its own set of problems), in which case, it would be illegal to use that struct in any situation that required init
. So, it might be possible to then have a default constructor which could run arbitrary code at runtime, but what the exact consequences of that would be, I don't know. However, I'm sure that there are cases where people would want to have a default constructor that would require init
and therefore wouldn't work, because it had been @disabled (things like declaring arrays of the type would probably be one of them).
So, as you can see, by doing what D has done with init
, it's made the whole question of default constructors much more complicated and problematic than it is in other languages.
The normal way to get something akin to default construction is to use a static opCall
. Something like
struct S
{
static S opCall()
{
//Create S with the values that you want and return it.
}
}
Then whenever you use S()
- e.g.
auto s = S();
then the static opCall
gets called, and you get a value that was created at runtime. However, S.init
will still be used any place that it was before (including S s;
), and the static opCall
will only be used when S()
is used explicitly. But if you couple that with @disable this()
(which disables the init
property), then you get something akin to what I described earlier where we might have default constructors with an @disabled init
.
We may or may not end up with default constructors being added to the language eventually, but there are a number of technical problems with adding them due to how init
and the language work, and Walter Bright doesn't think that they should be added. So, for default constructors to be added to the language, someone would have to come up with a really compelling design which appropriately resolves all of the issues (including convincing Walter), and I don't expect that to happen, but we'll see.