November 30, 2012
On Friday, 30 November 2012 at 03:21:24 UTC, Walter Bright wrote:
> So copying them is always an unsurprising bit copy.


Is this a speed thing, or is there a deeper reason?


If it's b/c of performance, IMO the compiler should be taking care of figuring out such things internally, not forcing the programmer to write complicated code.
November 30, 2012
On 11/29/2012 11:10 PM, Jonathan M Davis wrote:
>  [...]

You're right, I had overlooked the point that having no default constructor means that the default construction will *always* succeed. This is a large simplification.

Frankly, non-trivial default construction has always smelled like a bad practice to me, though it's not always obvious why.
November 30, 2012
On Friday, November 30, 2012 14:27:56 Walter Bright wrote:
> On 11/29/2012 11:10 PM, Jonathan M Davis wrote:
> > [...]
> 
> You're right, I had overlooked the point that having no default constructor means that the default construction will *always* succeed. This is a large simplification.
> 
> Frankly, non-trivial default construction has always smelled like a bad practice to me, though it's not always obvious why.

You can easily have default construction which is relatively trivial which cannot be done in an init value. For instance, std.datetime.SysTime has TimeZone member, and because it's a class, it can't be initialized to anything other than null at compile time. It would be great if it could default to LocalTime, but that's just not possible with how init and construction works in D. On the other hand, if structs were default-constructed, it would be a non-issue, because the default constructor could initialize the TimeZone member.

init buys us a _lot_, but there are definitely cases where having default construction at runtime would be very beneficial. Being able to construct structs with no-param constructors partially fixes that issue, but we can't completely fix it and will just have to work around the remaining use cases where the lack of default construction causes issues.

- Jonathan M Davis
November 30, 2012
On Friday, 30 November 2012 at 03:27:57 UTC, Walter Bright wrote:
> Frankly, non-trivial default construction has always smelled like a bad practice to me, though it's not always obvious why.



If that's the case, then we need to get rid of postblits entirely.

They don't make sense if default-valued structs are meant to be bit-copyable.

Consider:

size_t n;

struct S { this(this) { n++; } }

void main()
{
	auto s1 = S();
	auto s2 = S.init;
	const s3 = const(S)();
	immutable s4 = immutable(S)();
	enum s5 = S();

	auto t1 = s1;
	auto t2 = s2;
	auto t3 = s3;
	auto t4 = s4;
	auto t5 = s5;

	// What's 'n' supposed to be now, and why?
}
November 30, 2012
On Friday, 30 November 2012 at 03:27:57 UTC, Walter Bright wrote:
> On 11/29/2012 11:10 PM, Jonathan M Davis wrote:
>> [...]
>
> You're right, I had overlooked the point that having no default constructor means that the default construction will *always* succeed. This is a large simplification.
>
> Frankly, non-trivial default construction has always smelled like a bad practice to me, though it's not always obvious why.

Just so we're clear, what we are asking for (or at least, what I'm asking for) isn't even *default* construction, but a way of calling a constructor that takes no arguments.

If:
"auto a = T(5);"
calls a constructor, then why can't:
"auto a = T();"
also call a constructor?

I'll repeat that I think that D's T.init semantics, and move abilities, are a great tool, but the cost of for we are paying for it is un-necessarily high, and unwarranted.

--------
The *only* reason I'd see against it, would be the initial confusion to C++ newcomers, but I mean: they already have to learn T.init and postblit anyways. They just have to learn it's a different language.

Related:
Why doesn't:
"auto a = int(5);"
work? and, even more importantly, why don't we have this? :
"int* p = new int (5);"
The fact that we have "T()/T(arg)", but not "int()/int(arg)" makes *zero* sense to me. THAT has been a source of initial confusion when I moved to D.
November 30, 2012
On Friday, 30 November 2012 at 03:27:57 UTC, Walter Bright wrote:
> On 11/29/2012 11:10 PM, Jonathan M Davis wrote:
>> [...]
>
> You're right, I had overlooked the point that having no default constructor means that the default construction will *always* succeed. This is a large simplification.
>
> Frankly, non-trivial default construction has always smelled like a bad practice to me, though it's not always obvious why.

Just so we're clear, what we are asking for (or at least, what I'm asking for) isn't even *default* construction, but a way of calling a constructor that takes no arguments.

If:
"auto a = T(5);"
calls a constructor, then why can't:
"auto a = T();"
also call a constructor?

I'll repeat that I think that D's T.init semantics, and move abilities, are a great tool, but the cost of for we are paying for it is un-necessarily high, and unwarranted.

--------
The *only* reason I'd see against it, would be the initial confusion to C++ newcomers, but I mean: they already have to learn T.init and postblit anyways. They just have to learn it's a different language.

Related:
Why doesn't:
"auto a = int(5);"
work? and, even more importantly, why don't we have this? :
"int* p = new int (5);"
The fact that we have "T()/T(arg)", but not "int()/int(arg)" makes *zero* sense to me. THAT has been a source of initial confusion when I moved to D.
November 30, 2012
On 29/11/2012 12:10, Maxim Fomin wrote:
> On Thursday, 29 November 2012 at 10:41:46 UTC, Mehrdad wrote:
>> I'm just not understanding the whole "the default construction of a
>> struct should be a compile time creature, not a runtime one".
>>
>>
>>
>> Don't you have to initialize the struct with zero's either way?
>>
>> So either way, you're going to have to initialize it... so no perf
>> increase in any way. Why prevent the user from default-initializing it
>> the way he wants to?
>
> Every type has a CT-known default initializer, even classes have (null).
> If structures had a runtime one, this would break code (especially
> templates and CTFE) which relies on knowing something about constant
> default instance of a type at CT.
>
> extern bool foo();
>
> struct S
> {
>    int i;
>    this() {
>      i = foo() ? 1 : -1;
>    }
> }
> ---------
> S s;
> dosmth(s);
> ---------
> //somewhere in Phobos
>
> void dosmth(T) (T obj)
> {
>    T val; // is i 0, -1 or 1 ?
> }

I think s.i and val.i should be zero. S.this() should never be called implicitly IMO, but instead like this:

// runtime code
S s = S();

These two should always be equivalent, and compile-time evaluated:

S s;
S s = S.init;

If we actually need non-trivial compile-time 'default' constructors, they should have a different syntax:

// runtime ctors
this();
this(int x = 0);
this(T...)(T args);

// CT ctor (if actually needed)
default this(){...}

S.init would imply a call to the above CT constructor.
November 30, 2012
11/30/2012 3:31 AM, Jonathan M Davis пишет:
> On Thursday, November 29, 2012 20:27:32 Dmitry Olshansky wrote:
>> 11/29/2012 7:24 AM, Walter Bright пишет:
>>> On 11/29/2012 4:47 AM, monarch_dodra wrote:
>>>> On Sunday, 25 November 2012 at 16:47:08 UTC, Dmitry Olshansky wrote:
>>>>> Thoughts?
>>>>
>>>> I don't know about "killing" T(), but I think there *needs* to be an
>>>> (easy) mechanism to declare ***and*** run-time initialize an object, in
>>>> a single and comprehensive line.
>>>>
>>>> I had proposed something 2 months ago here:
>>>> http://forum.dlang.org/thread/bvuquzwfykiytdwsqkky@forum.dlang.org
>>>>
>>>> The proposal wasn't perfect, but still. We need to figure something
>>>> out...
>>>
>>> The original idea is that there should be *no such thing* as default
>>> construction of a struct as being anything other than T.init. The
>>> default construction of a struct should be a compile time creature, not
>>> a runtime one.
>>
>> Okay let it be. I'm not against having a defined blank default
>> constructed object.
>>
>> Just don't make T() mean it, please!
>
> I'm all for T() meaning T.init if T doesn't have a static opCall, but T()
> shouldn't be guaranteed to be T.init. I'd very much like to see code like
>
> auto t = T();
>
> to continue to work regardless of whether T has a static opCall or not.

And what you'd expect 't' to be then? And why such code is useful anyway? The only sane way I see is to make it an explicit call of 0-arg constructor then one can safely assume:
1. t is of type T
2. t is properly constructed and not some invalid state like T.init may be

Currently with opCall it could be anything otherwise it ends up T.init or compiler error if T is a built-in type.


> But it
> should be able to have a static opCall, and T() should work if it doesn't have
> one. Assuming that T() means T.init makes no sense.

Why in the nine hells we have static opCall to begin with?
Probably to workaround 0-argument ctor situation. I haven't seen a better or at least sensible use case. Yet it has great potential for abuse.

(and the fact that compiler picks opCall first (static or not) instead of constructor doesn't help matters much)


-- 
Dmitry Olshansky
November 30, 2012
On Friday, November 30, 2012 23:34:04 Dmitry Olshansky wrote:
> 11/30/2012 3:31 AM, Jonathan M Davis пишет:
> > I'm all for T() meaning T.init if T doesn't have a static opCall, but T()
> > shouldn't be guaranteed to be T.init. I'd very much like to see code like
> > 
> > auto t = T();
> > 
> > to continue to work regardless of whether T has a static opCall or not.
> 
> And what you'd expect 't' to be then? And why such code is useful
> anyway? The only sane way I see is to make it an explicit call of 0-arg
> constructor then one can safely assume:
> 1. t is of type T
> 2. t is properly constructed and not some invalid state like T.init may be
> 
> Currently with opCall it could be anything otherwise it ends up T.init or compiler error if T is a built-in type.

If

auto t = T();

works then you don't have to care whether the type has a static opCall or not. You get the most valid default-constructed object that there is (or at least, the closest thing that there is to a default-constructed object). If that's init, then it's init. If it's static opCall, then it's static opCall. I don't want to have to care which it is. Also, if I see

T t;

I'm likely to think that was supposed to be initialized, but the programmer forgot, whereas with

auto t = T();

it's clear that that it was intended to be initialized to whatever T() is (be it T.init or the result of a static opCall), and it's clear that the programmer didn't forget to initialize it.

> > But it
> > should be able to have a static opCall, and T() should work if it doesn't
> > have one. Assuming that T() means T.init makes no sense.
> 
> Why in the nine hells we have static opCall to begin with?
> Probably to workaround 0-argument ctor situation. I haven't seen a
> better or at least sensible use case. Yet it has great potential for abuse.
> 
> (and the fact that compiler picks opCall first (static or not) instead
> of constructor doesn't help matters much)

I don't know why we have static opCall, but it's used heavily for no-arg constructors, and it's extremely useful to be able to have those.

Interestingly enough though, if you were to give all of your class static opCalls, it would become possible to construct classes as if they were structs and not care which you're dealing with (similar to what std.container.make tries to do). I don't know that that's necessarily a good idea, but it's at least another potentially useful way to use static opCall other than providing no-arg constructors to structs.

- Jonathan M Davis
December 01, 2012
On 11/30/2012 3:31 PM, Mehrdad wrote:
> If that's the case, then we need to get rid of postblits entirely.

The only justification I've ever been able to come up with for postblits is implementing a reference counting type.