View mode: basic / threaded / horizontal-split · Log in · Help
October 10, 2012
Re: What is the case against a struct post-blit default constructor?
On 10/10/12 13:27, Timon Gehr wrote:
> On 10/10/2012 12:45 PM, Don Clugston wrote:
>> On 10/10/12 11:21, Jonathan M Davis wrote:
>>> On Monday, October 08, 2012 18:47:43 Malte Skarupke wrote:
>>>> So I really can't think of a reason for why you wouldn't want
>>>> this. Yet this discussion has happened several times already.
>>>> There is clear demand for it and very good reasons, such as those
>>>> mentioned in all the linked discussions.
>>>>
>>>> So why is this being rejected?
>>>
>>> It buys you pretty much nothing. There are plenty of places in the
>>> language
>>> where init is required (e.g. member variables that can't be directly
>>> initialized and the elements in an array). So, init _will_ be used
>>> regardless
>>> of what you do with the default constructor. If you want to prevent
>>> that, then
>>> you need to disable init, which we can already do. But you're not
>>> going to get
>>> those things initialized with the default constructor, which kind of
>>> defeats
>>> the purpose of the default constructor. If you can't guarantee that
>>> every
>>> instance which isn't explicitly constructed is default constructed, then
>>> what's the point?
>>
>> Of course there would be no point.
>> You have not answered the question. The issue is, WHY can we not
>> guarantee that that the struct default constructor is called?
>>
>
> Because the current language does not. :o)
>
> Because that would imply disabling .init.
>   - Which defeats the .init idiom in generic code. I am not convinced
>     that we need that though.

I don't like the .init idiom, I'm not sure it actually works. The 
semantics of .init aren't well defined. There is no guarantee that it is 
a valid value of the type.


 (T x)=>... does the job just fine, and
>     .init has issues, as eg. int.init has special implicit conversion
>     rules that not all members of the type int share.
>   - Need a new (unsafe) language feature in order to be able to
>     implement eg. 'emplace'.

Exactly.


>> I have a vague memory that Walter mentioned a technical difficulty once
>> but I don't remember anything about what it was.
>>
>> I can't imagine what it would be.
>
> +1.
>
>> Even in the worst case, it would be
>> possible to run CTFE on the default constructor in order to create
>> .init. This would limit the default constructor to things which are
>> CTFEable, but even that would still be useful for templated structs.
>>
>
> You could run CTFE on the default constructor iff .init is requested,
> but I don't really see the point. What would be the benefit?

You could have more complicated relationships between members.

struct W
{
  int x;
  int y;
}

W bar(T)() { return W(T.sizeof, T.alignof); }

struct Foo(T)
{
   int m;
   int n;
   int k;

   this() {
     W w = bar!(T)();
     m = w.x;
     n = w.y;
     k = abs(64 - m - n);
   }
}

of course it gains in value as bar!()() gets more complicated.


>> Really, there does not seem to me to be any point in having an invariant
>> for a struct, without a default constructor.
>>
>
> One can use a dented invariant.
>
> struct S{
>      bool valid = false;
>      // ...
>      invariant(){ if(valid) assert(...); }
>      void establishInvariant()out{assert(valid);}body{...}
>}

Yes, you have to do something like that. It's absolute garbage. When you 
have a hack like that, I don't see the point of having invariants in the 
language.


>> BTW .init doesn't really work for nested structs anyway. There are
>> several open bugs related to that.
>>
>
> That is true, what are the plans for that?

Don't know.
October 10, 2012
Re: What is the case against a struct post-blit default constructor?
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
October 10, 2012
Re: What is the case against a struct post-blit default constructor?
Thank you for explaining.
See comments inline.

On Wednesday, 10 October 2012 at 18:12:13 UTC, Jonathan M Davis 
wrote:
> 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.
>

I understand the idea of default initialization. I was more 
interested in the machinery and implementation details :) So 
let's dive in into those details:
Arrays - without changing existing syntax we can use these 
semantics:

auto a = new int[](5); // compiler calls T() for each instance
int[12] b; // ditto

This would be same as in C++. We could also expand the syntax and 
allow:
auto b = new int[](5, 9); // init all instances to 9
auto b = new int[](5, int (int index) { return index; });
initializes each member via a function call.
This can be generalized for multi dimensions.

> 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.

So for classes .init is null which complicates non-nullable 
classes. It seems the "solution" (more like a hack IMO) of 
@disable _breaks_ the .init guaranty in the language.

>
> 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.
>

C++ is insanely bad here mainly due to [virtual?] MI which 
doesn't affect D
and Java _allows_ virtual methods in constructors, which I think 
is also "fixed" in the latest c++ standard. I don't know about 
the ordering problems you mention but AFAIK the complication 
arises with MI, not default initialization. It's just a matter of 
properly defining the inheritance semantics.



> 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.

You talk about:
class C {
immutable T val; // what to do here?
this() { ... }
}

This can be solved be either requiring a ctor call at # or if 
none specified call T(), or we can require the init to happen in 
the ctor a-la C++ semantics.




>
> 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.

Personally, I'd just get remove this feature from the lanuage, 
tuples are a far better design for returning multiple values and 
even with this feature intact, we could always use the default 
no-arg constructor.
E.g
void foo(out T val);
becomes:
void foo(out T val = T());

>
> 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?
>

I don't get this example. If foo throws than the calling code 
will get control. How would you ever get to read that garbage in 
aa[5]? The surrounding try catch block should take care of this 
explicitly anyway.

E.g.
try {
 aa[5] = foo(); // foo throws
 // ## do something with aa[5], this won't happen
} catch {
// Please handle aa[5] here explicitly.
//
}
// @@ do something with aa[5], works due to the explicit fix in 
the catch.




> 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.
>

Isn't @disable breaks those algorithms in phobos anyway? how 
would that work for non-nullable classes?
To answer the above question, I'd say there's nothing wrong with 
init to void. This is what happens anyway since the .init isn't 
used and the optimizer will optimize it away.

> 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

Again, thanks for the explanation. I have to say that on a 
general level I have to agree with Don's post and I don't see how 
the .init idiom generally "works" or is useful. I can't see 
anything in the above examples that shows that .init is 
absolutely required and we can't live without it. The only thing 
that worries me here is the reliance of the runtime/phobos on 
.init.
October 10, 2012
Re: What is the case against a struct post-blit default constructor?
On Wednesday, October 10, 2012 21:11:29 foobar wrote:
> Arrays - without changing existing syntax we can use these
> semantics:
> 
> auto a = new int[](5); // compiler calls T() for each instance
> int[12] b; // ditto

And what on earth does that buy you over init? That's what init _does_. And 
since the value needs to be known at compile time (otherwise arrays won't work 
for directly initializing _anything_ that needs to be initialized at compile 
time), a constructor really doesn't buy you anything here at all. init 
provides a nice, consistent way of initializing or assigning a value to a 
variable as well as providing a consistent default state for a type when you 
need to be able to set a variable of that type to a consistent, safe state 
(e.g. with std.algorithm.move). And as built-in types don't _have_ 
constructors, there's no other way for generic code to generically initialize 
anything.

> I don't get this example. If foo throws than the calling code
> will get control. How would you ever get to read that garbage in
> aa[5]? The surrounding try catch block should take care of this
> explicitly anyway.
> 
> E.g.
> try {
> aa[5] = foo(); // foo throws
> // ## do something with aa[5], this won't happen
> } catch {
> // Please handle aa[5] here explicitly.
> //
> }
> // @@ do something with aa[5], works due to the explicit fix in
> the catch.

It's a compiler (druntime?) bug. T.init is inserted at aa[5] regardless of 
whether foo() returns or not. It's just the assigned to the correct result of 
foo succeeds. So, if an exception is thrown, you end up with a value at aa[5] 
when you're not supposed to:

http://d.puremagic.com/issues/show_bug.cgi?id=3825

The fact that we have init saves us from it being set to garbage. In general, 
having init saves us from garbage where someone screws up, making behavior 
deterministic and therefore more easily caught, debugged, and fixed. It also 
saves us from the compiler complaining about stuff not being initialized when 
it really was like Java does.

> Isn't @disable breaks those algorithms in phobos anyway?

Which is why @disable is kind of a sucky idea. But as long as init exists for 
a type, functions such as std.algorithm.move can use it. Without it, they're 
pretty much screwed. So, using @disable will restrict what you can do with a 
type, but it least sane types can take advantage of such functions. Without 
init, _none_ could, or if they did, it would be unsafe.

> To answer the above question, I'd say there's nothing wrong with
> init to void. This is what happens anyway since the .init isn't
> used and the optimizer will optimize it away.

Using void is dangerous and should be avoided unless absolutely necessary, 
otherwise you run a high risk of ending up with garbage values, giving you 
non-determinstic behavior, which tends to lead to very nasty, hard-to-find 
bugs.

> Again, thanks for the explanation. I have to say that on a
> general level I have to agree with Don's post and I don't see how
> the .init idiom generally "works" or is useful. I can't see
> anything in the above examples that shows that .init is
> absolutely required and we can't live without it. The only thing
> that worries me here is the reliance of the runtime/phobos on
> .init.

init avoids all kind of initialization problems and is an absolute godsend for 
generic code - so much so that letting it be used for compile time reflection 
and type inferrence even when disabled (though it still couldn't be used in 
actual code) is under discussion. It would actually be pretty bad not to have 
init (which is why @disable sucks). I, for one, am _very_ glad that we have 
init. I'm guessing that you haven't written much generic code in D if you 
think that init should go away. You pretty much _need_ something like init for 
a lot of the stuff that you have to do in template constraints and whatnot.

D's use of init is far superior to either C++ or Java's approach IMHO. But 
regardless of its various pros or cons, it's here to stay.

- Jonathan M Davis
October 10, 2012
Re: What is the case against a struct post-blit default constructor?
On Wednesday, October 10, 2012 22:40:50 Jonathan M Davis wrote:
> D's use of init is far superior to either C++ or Java's approach IMHO. But
> regardless of its various pros or cons, it's here to stay.

Something to remember here is that there are multiple ways to solve some of 
these problems. Each has their pros and cons. D has gone with init and IMHO 
greatly from it. You can undoubtedly find alternate ways to deal with pretty 
much every use case for init. After all, other languages went with other 
solutions (each with their own pros and cons). But init is what D chose to go 
with, so regardless of whether it's the best choice or not, it's what we have, 
and it's far too late to change it now.

So, while you may prefer another solution, because you prefer a different set 
of pros and cons, that doesn't mean that D's choice was a bad one. It just 
means that what it prioritizes isn't necessarily the same as what you 
prioritize.

- Jonathan M Davis
October 11, 2012
Re: What is the case against a struct post-blit default constructor?
Hi, thanks for the detailed answers.

So I am very much for keeping init. I just want that if the user 
specifies a default constructor, it is being called after the 
value has been initialized to init.

> On Wednesday, October 10, 2012 21:11:29 foobar wrote:
>> Arrays - without changing existing syntax we can use these
>> semantics:
>> 
>> auto a = new int[](5); // compiler calls T() for each instance
>> int[12] b; // ditto
>
> And what on earth does that buy you over init? That's what init 
> _does_. And
> since the value needs to be known at compile time (otherwise 
> arrays won't work
> for directly initializing _anything_ that needs to be 
> initialized at compile
> time), a constructor really doesn't buy you anything here at 
> all. init
> provides a nice, consistent way of initializing or assigning a 
> value to a
> variable as well as providing a consistent default state for a 
> type when you
> need to be able to set a variable of that type to a consistent, 
> safe state
> (e.g. with std.algorithm.move). And as built-in types don't 
> _have_
> constructors, there's no other way for generic code to 
> generically initialize
> anything.

"What does this buy you over init?" This would help with structs 
that can't be initialized to a valid value at compile time.
So what I expect to happen in a line like
S[12] s;
Is that they all get blitted to init, followed by a loop that 
calls the default constructor on each of these. If there is no 
default constructor, they stay at init and the loop doesn't 
happen.
This would work with user defined types and with built-in types.
If a constructor throws an exception, then the array is correctly 
initialized up to the element before the throwing one. The 
remaining elements are at init.

For member variables I expect this to happen:
If one of your member variables has a default constructor but you 
do not, then the compiler will generate an empty default 
constructor for you. This gives your object the same behavior in 
arrays as described above, which means that the member variable 
will always be correctly initialized using both init and the 
deafult constructor.
If one of your member variables has a default constructor, and so 
do you, then the member variable's default constructor is called 
before your default constructor.
If an exception is thrown, the containing object remains at the 
state it is in (most likely init)
If you do not want the default constructor of your member 
variable to be called, you can declare it as S s = S.init;

For emplace I expect that the default constructor gets called.

For std.algorithm.move you'd have to define which state the 
source object is in after moving. Since it's a destructive move, 
I'd expect the object to be at init, as if a destructor had been 
called. You could implement that with a tiny change to the 
current implementation. (memcpy from init instead of a statically 
allocated instance)

This should cover all the cases that you mentioned. It is highly 
performant (in fact this is very close to the behavior in C++, 
but faster because it uses init) and it won't affect structs that 
can be initialized at compile time. It will only be used for 
cases where you need to do things at runtime, and the users have 
the full ability to shoot themselves in the foot if they want to.
October 11, 2012
Re: What is the case against a struct post-blit default constructor?
On 10/8/2012 9:47 AM, Malte Skarupke wrote:
> So why is this being rejected?

So S.init is a valid instance of S.
October 11, 2012
Re: What is the case against a struct post-blit default constructor?
On 10/10/12 6:45 AM, Don Clugston wrote:
> On 10/10/12 11:21, Jonathan M Davis wrote:
>> On Monday, October 08, 2012 18:47:43 Malte Skarupke wrote:
>>> So I really can't think of a reason for why you wouldn't want
>>> this. Yet this discussion has happened several times already.
>>> There is clear demand for it and very good reasons, such as those
>>> mentioned in all the linked discussions.
>>>
>>> So why is this being rejected?
>>
>> It buys you pretty much nothing. There are plenty of places in the
>> language
>> where init is required (e.g. member variables that can't be directly
>> initialized and the elements in an array). So, init _will_ be used 				
>> regardless
>> of what you do with the default constructor. If you want to prevent
>> that, then
>> you need to disable init, which we can already do. But you're not
>> going to get
>> those things initialized with the default constructor, which kind of
>> defeats
>> the purpose of the default constructor. If you can't guarantee that every
>> instance which isn't explicitly constructed is default constructed, then
>> what's the point?
>
> Of course there would be no point.
> You have not answered the question. The issue is, WHY can we not
> guarantee that that the struct default constructor is called?

We could (after all, C++ does it). There are a few disadvantages to 
doing so, however.

1. Defining static data is more difficult. Currently, all static data is 
statically-initialized. With default constructors, we'd need to define 
the pre-construction state of such objects anyway, and then change the 
compiler to call constructors prior to main(). I find the current design 
simpler and easier to use.

2. Creating a temporary object cannot be anymore assumed to be a O(1), 
no-resources-allocated deal. Instead, generic code must conservatively 
assume that objects are always arbitrarily expensive to create. That 
makes some generic functions more difficult to implement.

3. Two-phase object destruction (releasing state and then deallocating 
memory), which is useful, is made more difficult by default 
constructors. Essentially the .init "pre-default-constructor" state 
intervenes in all such cases and makes it more difficult for language 
users to define and understand object states.

4. Same as above applies to an object post a move operation. What state 
is the object left after move? C++'s approach to this, forced by the 
existence of default constructors and other historical artifacts, has a 
conservative approach that I consider inferior to D's: the state of 
moved-from object is decided by the library, there's often unnecessary 
copying, and is essentially unspecified except that "it's valid" so the 
moved-from object can continue to be used. This is in effect a back-door 
introduction of a "no-resources-allocated" state for objects, which is 
what default constructors so hard tried to avoid in the first place.

5. There are a few minor issues such as correct array creation etc. but 
I don't consider them decisive.

There are obvious disadvantages of the lack of a default constructor. I 
believe they are overcome by the advantages, although clearly reasonable 
people may disagree.

> I have a vague memory that Walter mentioned a technical difficulty once
> but I don't remember anything about what it was.
>
> I can't imagine what it would be. Even in the worst case, it would be
> possible to run CTFE on the default constructor in order to create
> ..init. This would limit the default constructor to things which are
> CTFEable, but even that would still be useful for templated structs.

Allowing a default constructor that's computable during compilation 
would be a very interesting idea.

> Really, there does not seem to me to be any point in having an invariant
> for a struct, without a default constructor.

Could you please give a few examples? (Honest question.) Most structures 
I define have an obvious quiescent state that vacuously satisfies the 
invariant. Exceptions that come to mind are: (a) value types that must 
always allocate something on the heap, see e.g. the contortions in 
std.container; (b) values as permits (the existence of the value 
guarantees a resource has been secured, as in scoped locks on mutexes).


Andrei
October 11, 2012
Re: What is the case against a struct post-blit default constructor?
Le 11/10/2012 14:19, Andrei Alexandrescu a écrit :
>
> We could (after all, C++ does it). There are a few disadvantages to
> doing so, however.
>
> 1. Defining static data is more difficult. Currently, all static data is
> statically-initialized. With default constructors, we'd need to define
> the pre-construction state of such objects anyway, and then change the
> compiler to call constructors prior to main(). I find the current design
> simpler and easier to use.
>

CTFE is probably the answer here.

> 2. Creating a temporary object cannot be anymore assumed to be a O(1),
> no-resources-allocated deal. Instead, generic code must conservatively
> assume that objects are always arbitrarily expensive to create. That
> makes some generic functions more difficult to implement.
>

Temporary object are used to store temporary state coming from some 
computation. In this case, the computation create the complexity, the 
object isn't created via default constructor.

Or have you a use case in mind I don't think of ?

> 3. Two-phase object destruction (releasing state and then deallocating
> memory), which is useful, is made more difficult by default
> constructors. Essentially the .init "pre-default-constructor" state
> intervenes in all such cases and makes it more difficult for language
> users to define and understand object states.
>

This one is made worse by the current state. You have to assume 
everywhere that your struct can be .init

Even when it doesn't make any sense. RefCounted is a pathologic case of 
that.

> 4. Same as above applies to an object post a move operation. What state
> is the object left after move? C++'s approach to this, forced by the
> existence of default constructors and other historical artifacts, has a
> conservative approach that I consider inferior to D's: the state of
> moved-from object is decided by the library, there's often unnecessary
> copying, and is essentially unspecified except that "it's valid" so the
> moved-from object can continue to be used. This is in effect a back-door
> introduction of a "no-resources-allocated" state for objects, which is
> what default constructors so hard tried to avoid in the first place.
>

If we give struct a giveaway state (where the struct cannot be used 
unless it is reinitilized to a correct value) this problem disappear.

Except in the case 5. (and heap allocated struct in general), that in 
fact seems to me the major issue.

> 5. There are a few minor issues such as correct array creation etc. but
> I don't consider them decisive.
>
October 11, 2012
Re: What is the case against a struct post-blit default constructor?
Le 11/10/2012 14:19, Andrei Alexandrescu a écrit :
> Could you please give a few examples? (Honest question.) Most structures
> I define have an obvious quiescent state that vacuously satisfies the
> invariant. Exceptions that come to mind are: (a) value types that must
> always allocate something on the heap, see e.g. the contortions in
> std.container; (b) values as permits (the existence of the value
> guarantees a resource has been secured, as in scoped locks on mutexes).
>

invariant will explode at you face at runtime any time you use the 
struct wrong where a default constructor would have prevented such use 
in the first place.

Worse, the faulty case can be created at any place where the struct is 
used and is likely to create a problem.

In fact, such design rely on the well known « a good programmer don't do 
... » which is known to be a very good way to design hard to use and 
error prone constructs.
1 2 3 4 5
Top | Discussion index | About this forum | D home