What is the case against a struct post-blit default constructor?
Jonathan M Davis
jmdavisProg at gmx.com
Wed Oct 10 10:47:37 PDT 2012
On Wednesday, October 10, 2012 13:40:06 foobar wrote:
> Can you please elaborate on where the .init property is being
> relied on? This is an aspect of D I don't really understand.
> What's the difference between a no-arg ctor and one with args in
> relation to this requirement?
init is used anywhere and everywhere that an instance of a type needs to be
default-initialized. _One_ of those places is a local variable. Not all places
where an instance of an object needs to be created can be initialized by the
programmer. The prime example of this would be arrays. If you declare
auto i = new int[](5);
or
int[12] s;
all of the elements in the array need to be initialized or they'll be garbage,
and there's no way for the programmer to indicate what values they should be.
The whole point of init is to avoid having variables ever be garbage without
the programmer explicitly asking for it. And having a default constructor
wouldn't help one whit with arrays, because the values of their elements must
be known at compile time (otherwise you couldn't directly initialize member
variables or static variables or anything else which requires a value at
compile time with an array). With init, the compiler can take advantage of the
fact that it knows the init value at compile time to efficiently initialize the
array.
But even constructing objects sanely relies on init. All user-defined objects
are fully initialized to what their member variables are directly initialized
to before their constructors are even called. In the case of a struct, that's
the struct's init value. It's not for a class, because you can't have a class
separate from its reference (so it's the reference which gets the init value),
but the class still has a state equivalent to a struct's init value, and
that's the state that it has before any of its constructors are called.
If it weren't for that, you'd get the insanity that C++ or Java have with
regards to the state of objects prior to construction. C++ is particularly bad
in that each derived class is created in turn, meaning that when a constructor
is called, the object _is_ that class rather than the derived class that
you're ultimately constructing (which means that things can go horribly wrong
if you're stupid enough to call a virtual function from a constructor in C++).
I believe that Java handles that somewhat better, but it gets bizarre ordering
issues with regards to initializing member variables that cause problems if
you try and alter member variables from base classes inside of a derived
constructor. With D, the object is guaranteed to be in a sane state prior to
construction.
And without init, even if every place that an object is instantiated could be
directly initialized by the programmer (which it can't), then you would either
end up with garbage every time that a variable isn't directly initialized, or
you'd have to directly initialize them all. In order for D's construction
model to work, this would include directly initializing _all_ member variables
even if the constructor then set them to something else (which would actually
cause problems with const and immutable). And that would get _very_ annoying,
even if it would be preferable for the local variable to require explicit
initialization.
Another case where init is required is out parameters. All out parameters are
set to their init value when the function is called in order to avoid bugs
caused by reading the value of an out parameter before it's set within the
function. That wouldn't work at all without init.
One of the more annoying AA bugs makes it so that if the foo function in this
code
aa[5] = foo();
throws, then aa[5] gets set with a init value of the element type. While this
clearly shouldn't happen, imagine how much worse it would be if we didn't have
init, and that element got set to garbage?
There are probably other cases that I can't think of right now where init gets
used - probably in the runtime if nowhere else. Every place that could
possibly result in a variable being garbage _doesn't_ result in garbage,
because we have init.
And regardless of what the language does, there are definitely places where the
standard library takes advantage of init. It uses it a lot for type
inferrence, but it also uses it directly in places such as std.algorithm.move.
Without init, it would end up dealing with garbage values. It's also a
lifesaver in generic code, because without it, generic code _can't_ initialize
variables in many cases. Take something like
T t;
if(cond)
{
...
t = getValue();
...
}
else
{
...
t = getOtherValue();
...
}
How on earth could a generic function initialize t without T.init? void?
That's just begging for bugs when one the paths doesn't actually set t like
it's supposed to. It doesn't know anything about the type and therefore
doesn't know what a reasonable default value would be, so it can't possibly
initialize t properly.
I can understand prefering that local variables have to be directly
initialized by the programmer, but it just doesn't scale. Having init is
_far_more flexible and far more powerful. Any and every situation that might
need to initialize a variable can do it. Without init, that just isn't
possible.
- Jonathan M Davis
More information about the Digitalmars-d
mailing list