October 10, 2012
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
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
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
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
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
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
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
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
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
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