digitalmars.D - Error about disabled constructor when there is a custom one
- Minas Mina (23/23) Jan 22 2013 I have this structure:
- Dmitry Olshansky (18/37) Jan 22 2013 That is the reason I dislike D's struct constructors as they currently
- Minas Mina (8/8) Jan 22 2013 From Jonathan M Davis:
- Dmitry Olshansky (5/13) Jan 22 2013 I don't buy it either. 0-argument constructor have nothing to do with
- monarch_dodra (3/19) Jan 22 2013 What he said.
- Jonathan M Davis (10/23) Jan 22 2013 init prevents us from having _default_ constructors. As much as no-arg
- Simen Kjaeraas (6/7) Jan 23 2013 Add @disable this(); to make sure, though. Otherwise you're likely to
- Simen Kjaeraas (31/53) Jan 22 2013 You *are* using the default one. The one without parameters *is* the
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (7/23) Jan 22 2013 The default parameter values are not useful (or don't make sense) anymor...
- deadalnix (3/66) Jan 22 2013 Cheap workaround as you cannot new.
- Simen Kjaeraas (6/15) Jan 22 2013 Really? It's not like you cannot @disable T.init. Please show how this i...
- deadalnix (11/27) Jan 23 2013 Easy :
- Simen Kjaeraas (7/33) Jan 23 2013 Patently false. @disable this() makes this fail with "initializer requir...
- deadalnix (3/7) Jan 23 2013 move memcopy T.init . So it will be null.
- Simen Kjaeraas (11/18) Jan 23 2013 Try it. Without a destructor, move does not revert to T.init. With a
- deadalnix (5/23) Jan 23 2013 Ho that is awesome !
- Simen Kjaeraas (7/32) Jan 23 2013 Hm. The heap. That should be new NotNull!(int*), and
- deadalnix (4/40) Jan 23 2013 I was more thinking about slice and stuff like that. struct on
- Minas Mina (2/2) Jan 23 2013 Why is it required to have the .init property? Where is it useful
- monarch_dodra (37/39) Jan 23 2013 .init is very useful in the sense that it represents the raw
- Minas Mina (5/44) Jan 23 2013 Really good explanation. Thank you. So @disabled default
I have this structure: struct Scene { Array!Surface objects; // objects in the scene Array!Light lights; // lights in the scene /*private*/ BVHNode root; // root node of the BVH tree disable this(); // disable the default constructor because space needs to be reserved for objects and lights this(size_t objectReserveSpace = 20, size_t lightReserveSpace = 3) { objects.reserve(objectReserveSpace); lights.reserve(lightReserveSpace); } } auto scene = Scene(); // error about disabled constructor Yes, the default constructor is disabled BUT I am not using that one. I want to use the other one - the custom constructor. I guess it signals an error because it has those defaults parameters. But shouldn't the compiler choose that one?
Jan 22 2013
22-Jan-2013 22:45, Minas Mina пишет:I have this structure: struct Scene { Array!Surface objects; // objects in the scene Array!Light lights; // lights in the scene /*private*/ BVHNode root; // root node of the BVH tree disable this(); // disable the default constructor because space needs to be reserved for objects and lights this(size_t objectReserveSpace = 20, size_t lightReserveSpace = 3) { objects.reserve(objectReserveSpace); lights.reserve(lightReserveSpace); } } auto scene = Scene(); // error about disabled constructorThat is the reason I dislike D's struct constructors as they currently stand. Behold as the above is always rewritten to: auto scene = Scene.init; that is the same as Scene scene; And for Scene.init to work it has to have T.init for all of its members. You've hit what I think is the design bug w.r.t. conflating 0-argument constructor (including one with all default args) and T.init. Short answer is: use static opCall instead. In the meantime let us all pray some supreme gods so that 0-arg constructor support is introduced later on. (because opCall doesn't have some powers of constructors such as constructing const object)Yes, the default constructor is disabled BUT I am not using that one. I want to use the other one - the custom constructor. I guess it signals an error because it has those defaults parameters. But shouldn't the compiler choose that one?The official stance seems to be that the code shouldn't compile i.e. accepts-invalid. http://d.puremagic.com/issues/show_bug.cgi?id=3438 -- Dmitry Olshansky
Jan 22 2013
From Jonathan M Davis: "...At this point, I don't think that the situation with default constructors and structs is going to change. It's a result of requiring init properties for all types, and is thus a "forced fault" in the language..." Why does requiring init properties for all types results in this? What does that even mean?
Jan 22 2013
23-Jan-2013 00:28, Minas Mina пишет:From Jonathan M Davis: "...At this point, I don't think that the situation with default constructors and structs is going to change. It's a result of requiring init properties for all types, and is thus a "forced fault" in the language..." Why does requiring init properties for all types results in this? What does that even mean?I don't buy it either. 0-argument constructor have nothing to do with requiring .init. -- Dmitry Olshansky
Jan 22 2013
On Tuesday, 22 January 2013 at 21:12:50 UTC, Dmitry Olshansky wrote:23-Jan-2013 00:28, Minas Mina пишет:What he said.From Jonathan M Davis: "...At this point, I don't think that the situation with default constructors and structs is going to change. It's a result of requiring init properties for all types, and is thus a "forced fault" in the language..." Why does requiring init properties for all types results in this? What does that even mean?I don't buy it either. 0-argument constructor have nothing to do with requiring .init.
Jan 22 2013
On Wednesday, January 23, 2013 01:12:45 Dmitry Olshansky wrote:23-Jan-2013 00:28, Minas Mina пишет:init prevents us from having _default_ constructors. As much as no-arg constructors are normally default constructors, they're technically separate concepts. static opCall shows us that it's perfectly possible to have no-arg constructors. However, there's a certain danger in simply making a no-arg constructor for structs not be a default constructor, as pretty much anyone coming from another OO language will expect it to be a default constructor. For the most part, static opCall solves the problem. - Jonathan M DavisFrom Jonathan M Davis: "...At this point, I don't think that the situation with default constructors and structs is going to change. It's a result of requiring init properties for all types, and is thus a "forced fault" in the language..." Why does requiring init properties for all types results in this? What does that even mean?I don't buy it either. 0-argument constructor have nothing to do with requiring .init.
Jan 22 2013
On 2013-01-22, 22:28, Jonathan M Davis wrote:For the most part, static opCall solves the problem.Add disable this(); to make sure, though. Otherwise you're likely to forget the parentheses somewhere, and have a bug. -- Simen
Jan 23 2013
On 2013-01-22, 19:45, Minas Mina wrote:I have this structure: struct Scene { Array!Surface objects; // objects in the scene Array!Light lights; // lights in the scene /*private*/ BVHNode root; // root node of the BVH tree disable this(); // disable the default constructor because space needs to be reserved for objects and lights this(size_t objectReserveSpace = 20, size_t lightReserveSpace = 3) { objects.reserve(objectReserveSpace); lights.reserve(lightReserveSpace); } } auto scene = Scene(); // error about disabled constructor Yes, the default constructor is disabled BUT I am not using that one. I want to use the other one - the custom constructor. I guess it signals an error because it has those defaults parameters. But shouldn't the compiler choose that one?You *are* using the default one. The one without parameters *is* the default constructor, and you are calling the constructor without parameters. One could argue that the compiler should choose the other constructor, and even that having default constructors is reasonable. However, D has gone the route of not having default constructors for structs. Instead, structs are defined to be trivially constructible from T.init. The workaround is to use static opCall: struct Scene { Array!Surface objects; Array!Light lights; /*private*/ BVHNode root; disable this(); this(size_t objectReserveSpace = 20, size_t lightReserveSpace = 3) { objects.reserve(objectReserveSpace); lights.reserve(lightReserveSpace); } static Scene opCall(size_t objectReserveSpace = 20, size_t lightReserveSpace = 3) { return Scene(objectReserveSpace, lightReserveSpace); } } -- Simen
Jan 22 2013
On 01/22/2013 11:07 AM, Simen Kjaeraas wrote:The workaround is to use static opCall: struct Scene { Array!Surface objects; Array!Light lights; /*private*/ BVHNode root; disable this();That line must still be removed.this(size_t objectReserveSpace = 20, size_t lightReserveSpace = 3)The default parameter values are not useful (or don't make sense) anymore: this(size_t objectReserveSpace, size_t lightReserveSpace){ objects.reserve(objectReserveSpace); lights.reserve(lightReserveSpace); } static Scene opCall(size_t objectReserveSpace = 20, size_t lightReserveSpace = 3) { return Scene(objectReserveSpace, lightReserveSpace);Luckily that line calls the constructor, not itself and avoids an infinite loop. :) Ali
Jan 22 2013
On Tuesday, 22 January 2013 at 19:07:56 UTC, Simen Kjaeraas wrote:On 2013-01-22, 19:45, Minas Mina wrote:Which incompatible with the desire of a NonNull construct.I have this structure: struct Scene { Array!Surface objects; // objects in the scene Array!Light lights; // lights in the scene /*private*/ BVHNode root; // root node of the BVH tree disable this(); // disable the default constructor because space needs to be reserved for objects and lights this(size_t objectReserveSpace = 20, size_t lightReserveSpace = 3) { objects.reserve(objectReserveSpace); lights.reserve(lightReserveSpace); } } auto scene = Scene(); // error about disabled constructor Yes, the default constructor is disabled BUT I am not using that one. I want to use the other one - the custom constructor. I guess it signals an error because it has those defaults parameters. But shouldn't the compiler choose that one?You *are* using the default one. The one without parameters *is* the default constructor, and you are calling the constructor without parameters. One could argue that the compiler should choose the other constructor, and even that having default constructors is reasonable. However, D has gone the route of not having default constructors for structs. Instead, structs are defined to be trivially constructible from T.init.The workaround is to use static opCall: struct Scene { Array!Surface objects; Array!Light lights; /*private*/ BVHNode root; disable this(); this(size_t objectReserveSpace = 20, size_t lightReserveSpace = 3) { objects.reserve(objectReserveSpace); lights.reserve(lightReserveSpace); } static Scene opCall(size_t objectReserveSpace = 20, size_t lightReserveSpace = 3) { return Scene(objectReserveSpace, lightReserveSpace); } }Cheap workaround as you cannot new.
Jan 22 2013
On 2013-45-23 04:01, deadalnix <deadalnix gmail.com> wrote:On Tuesday, 22 January 2013 at 19:07:56 UTC, Simen Kjaeraas wrote:Really? It's not like you cannot disable T.init. Please show how this is a problem for NonNull!T.One could argue that the compiler should choose the other constructor, and even that having default constructors is reasonable. However, D has gone the route of not having default constructors for structs. Instead, structs are defined to be trivially constructible from T.init.Which incompatible with the desire of a NonNull construct.Cheap workaround as you cannot new.Indeed. But it's what we've got. -- Simen
Jan 22 2013
On Wednesday, 23 January 2013 at 08:00:05 UTC, Simen Kjaeraas wrote:On 2013-45-23 04:01, deadalnix <deadalnix gmail.com> wrote:Easy : NonNull!T bar; // bar is null NonNull!T[] barArray; barArray.length = 5; // barArray contains null elements. NonNull!T bar = something; foo(move(bar)); bar; // bar is null \o/ In fact they is so much way to create null stuff here that it isn't even remotely difficult to create a NonNull that is null;On Tuesday, 22 January 2013 at 19:07:56 UTC, Simen Kjaeraas wrote:Really? It's not like you cannot disable T.init. Please show how this is a problem for NonNull!T.One could argue that the compiler should choose the other constructor, and even that having default constructors is reasonable. However, D has gone the route of not having default constructors for structs. Instead, structs are defined to be trivially constructible from T.init.Which incompatible with the desire of a NonNull construct.
Jan 23 2013
On 2013-43-23 09:01, deadalnix <deadalnix gmail.com> wrote:On Wednesday, 23 January 2013 at 08:00:05 UTC, Simen Kjaeraas wrote:Patently false. disable this() makes this fail with "initializer required for type".On 2013-45-23 04:01, deadalnix <deadalnix gmail.com> wrote:Easy : NonNull!T bar; // bar is nullOn Tuesday, 22 January 2013 at 19:07:56 UTC, Simen Kjaeraas wrote:Really? It's not like you cannot disable T.init. Please show how this is a problem for NonNull!T.One could argue that the compiler should choose the other constructor, and even that having default constructors is reasonable. However, D has gone the route of not having default constructors for structs. Instead, structs are defined to be trivially constructible from T.init.Which incompatible with the desire of a NonNull construct.NonNull!T[] barArray; barArray.length = 5; // barArray contains null elements.This is a known bug in the implementation of disable this().NonNull!T bar = something; foo(move(bar)); bar; // bar is null \o/Except it isn't. -- Simen
Jan 23 2013
On Wednesday, 23 January 2013 at 10:28:05 UTC, Simen Kjaeraas wrote:move memcopy T.init . So it will be null.NonNull!T bar = something; foo(move(bar)); bar; // bar is null \o/Except it isn't.
Jan 23 2013
On 2013-48-23 11:01, deadalnix <deadalnix gmail.com> wrote:On Wednesday, 23 January 2013 at 10:28:05 UTC, Simen Kjaeraas wrote:Try it. Without a destructor, move does not revert to T.init. With a destructor you get this: src\phobos\std\algorithm.d(1564): Error: variable std.algorithm.move!(NotNull!(int*)).move.empty initializer required for type NotNull!(int*) Error: template instance std.algorithm.move!(NotNull!(int*)) error instantiating This is what disable this does, and what it's supposed to do. -- Simenmove memcopy T.init . So it will be null.NonNull!T bar = something; foo(move(bar)); bar; // bar is null \o/Except it isn't.
Jan 23 2013
On Wednesday, 23 January 2013 at 10:55:43 UTC, Simen Kjaeraas wrote:On 2013-48-23 11:01, deadalnix <deadalnix gmail.com> wrote:Ho that is awesome ! Still many way of generating it on the heap, but it seems that the stack is getting better !On Wednesday, 23 January 2013 at 10:28:05 UTC, Simen Kjaeraas wrote:Try it. Without a destructor, move does not revert to T.init. With a destructor you get this: src\phobos\std\algorithm.d(1564): Error: variable std.algorithm.move!(NotNull!(int*)).move.empty initializer required for type NotNull!(int*) Error: template instance std.algorithm.move!(NotNull!(int*)) error instantiating This is what disable this does, and what it's supposed to do.move memcopy T.init . So it will be null.NonNull!T bar = something; foo(move(bar)); bar; // bar is null \o/Except it isn't.
Jan 23 2013
On 2013-14-23 13:01, deadalnix <deadalnix gmail.com> wrote:On Wednesday, 23 January 2013 at 10:55:43 UTC, Simen Kjaeraas wrote:Hm. The heap. That should be new NotNull!(int*), and (new NotNull!(int*)[1])[0]. The former is correctly not allowed, and the latter is a different manifestation of the same bug mentioned previously. Of course, with casts and pointers, anything is possible. -- SimenOn 2013-48-23 11:01, deadalnix <deadalnix gmail.com> wrote:Ho that is awesome ! Still many way of generating it on the heap, but it seems that the stack is getting better !On Wednesday, 23 January 2013 at 10:28:05 UTC, Simen Kjaeraas wrote:Try it. Without a destructor, move does not revert to T.init. With a destructor you get this: src\phobos\std\algorithm.d(1564): Error: variable std.algorithm.move!(NotNull!(int*)).move.empty initializer required for type NotNull!(int*) Error: template instance std.algorithm.move!(NotNull!(int*)) error instantiating This is what disable this does, and what it's supposed to do.move memcopy T.init . So it will be null.NonNull!T bar = something; foo(move(bar)); bar; // bar is null \o/Except it isn't.
Jan 23 2013
On Wednesday, 23 January 2013 at 13:26:47 UTC, Simen Kjaeraas wrote:On 2013-14-23 13:01, deadalnix <deadalnix gmail.com> wrote:I was more thinking about slice and stuff like that. struct on the heap in general are not very handy.On Wednesday, 23 January 2013 at 10:55:43 UTC, Simen Kjaeraas wrote:Hm. The heap. That should be new NotNull!(int*), and (new NotNull!(int*)[1])[0]. The former is correctly not allowed, and the latter is a different manifestation of the same bug mentioned previously. Of course, with casts and pointers, anything is possible.On 2013-48-23 11:01, deadalnix <deadalnix gmail.com> wrote:Ho that is awesome ! Still many way of generating it on the heap, but it seems that the stack is getting better !On Wednesday, 23 January 2013 at 10:28:05 UTC, Simen Kjaeraas wrote:Try it. Without a destructor, move does not revert to T.init. With a destructor you get this: src\phobos\std\algorithm.d(1564): Error: variable std.algorithm.move!(NotNull!(int*)).move.empty initializer required for type NotNull!(int*) Error: template instance std.algorithm.move!(NotNull!(int*)) error instantiating This is what disable this does, and what it's supposed to do.move memcopy T.init . So it will be null.NonNull!T bar = something; foo(move(bar)); bar; // bar is null \o/Except it isn't.
Jan 23 2013
Why is it required to have the .init property? Where is it useful and why was this decision made?
Jan 23 2013
On Wednesday, 23 January 2013 at 11:03:45 UTC, Minas Mina wrote:Why is it required to have the .init property? Where is it useful and why was this decision made?.init is very useful in the sense that it represents the raw object, *statically*. This is useful for declaring statics. It also makes constructors straightforward, in a simple 2 pass scheme (copy T.init, then run constructor). More importantly, the T.init state is also the one you are supposed to have post destruction (Or at least, calling a destructor on T.init is supposed to be safe). This allows for some *very* efficient and built-in move semantics: To move a into b, simply destroy b, memcopy a into b, memcopy T.init over a. Notice how massively simpler this is compared to C++'s explicit Rvalue references? I *dare* you to try doing that with C++. "DEFAULT" constructor breaks this entire scheme, as it implies that T.init is not an object's natural state. It jeopardizes a static destructible state. The problem is that this overlaps with having an "explicit constructor that takes no arguments". This is particularly problematic for objects that have pointers to payloads that represent "shallow" objects. AA's is a prime example of this: //---- void insert5(int[int] i) { i[5] = 5; } int[int] a; //Un-initialized T.init state int[int] b = [1 : 1]; //initialized int[int] c = [1 : 1]; c.remove(1); //Empty but initialized insert5(a); insert5(b); insert5(c); assert(a is null); //Oh no! assert(b == [1 : 1, 5 : 5]); //5 correctly appended assert(c == [5 : 5]); //5 correctly copied //---- In this example, I had to jump through loops to initialize c.
Jan 23 2013
On Wednesday, 23 January 2013 at 12:07:48 UTC, monarch_dodra wrote:On Wednesday, 23 January 2013 at 11:03:45 UTC, Minas Mina wrote:Really good explanation. Thank you. So disabled default constructor was just a "side effect" of doing those that you described.Why is it required to have the .init property? Where is it useful and why was this decision made?.init is very useful in the sense that it represents the raw object, *statically*. This is useful for declaring statics. It also makes constructors straightforward, in a simple 2 pass scheme (copy T.init, then run constructor). More importantly, the T.init state is also the one you are supposed to have post destruction (Or at least, calling a destructor on T.init is supposed to be safe). This allows for some *very* efficient and built-in move semantics: To move a into b, simply destroy b, memcopy a into b, memcopy T.init over a. Notice how massively simpler this is compared to C++'s explicit Rvalue references? I *dare* you to try doing that with C++. "DEFAULT" constructor breaks this entire scheme, as it implies that T.init is not an object's natural state. It jeopardizes a static destructible state. The problem is that this overlaps with having an "explicit constructor that takes no arguments". This is particularly problematic for objects that have pointers to payloads that represent "shallow" objects. AA's is a prime example of this: //---- void insert5(int[int] i) { i[5] = 5; } int[int] a; //Un-initialized T.init state int[int] b = [1 : 1]; //initialized int[int] c = [1 : 1]; c.remove(1); //Empty but initialized insert5(a); insert5(b); insert5(c); assert(a is null); //Oh no! assert(b == [1 : 1, 5 : 5]); //5 correctly appended assert(c == [5 : 5]); //5 correctly copied //---- In this example, I had to jump through loops to initialize c.
Jan 23 2013