www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - const attribute makes whole element const?

reply "Namespace" <rswhite4 googlemail.com> writes:
Why fail this code?
without "const" on "Name" it works fine.

http://dpaste.dzfl.pl/9fa0986a
Sep 08 2012
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 09/09/2012 01:16 AM, Namespace wrote:
 Why fail this code?
 without "const" on "Name" it works fine.

 http://dpaste.dzfl.pl/9fa0986a

const fields cannot be written to. This includes the case when the entire struct is written to at once.
Sep 08 2012
next sibling parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 09/09/2012 08:09 AM, Namespace wrote:
 On Saturday, 8 September 2012 at 23:18:14 UTC, Timon Gehr wrote:
 On 09/09/2012 01:16 AM, Namespace wrote:
 Why fail this code?
 without "const" on "Name" it works fine.

 http://dpaste.dzfl.pl/9fa0986a

const fields cannot be written to. This includes the case when the entire struct is written to at once.

But i assign the const attribute in the ctor.

The constructor is not involved in that syntax.
 So this behaviour doesn't  make any sense...

The problem is with the internal workings of the marked line below: struct Test { const string Name; // ... } Test[string] tests; // ... tests[4] = t4; // <-- tests[4] is an expression that automatically creates a default Test object and associates that default object with key 4. Then the line above becomes an assignment on top of that object, which can't happen because of the const member. Associative arrays must have an insert() function if it does not already exist: tests.insert(4, t4); // does this exist? Ali
Sep 09 2012
prev sibling next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 09/10/2012 02:05 AM, Namespace wrote:
 I had never problems with that in C++.

clang++ sez: error: cannot define the implicit default assignment operator for 'S', because non-static const member 'x' can't use default assignment operator
 If I have members which are const because they are assigned only one
 time and needs no other assignment, why should I declare this member not
 as const?

You can, but then you have to provide your own assignment operator and you have to work around the aforementioned bug that implies the assignment operator is not invoked for associative array index assignments.
 In the example I know exactly that I assign only one time a name to this
 struct, so why I should not declare it as const?

 Other example: you have a unique birthday date. This is const you cannot
 change it like a name or a telephone number.

Therefore I cannot be default-assigned to.
Sep 09 2012
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2012-09-10 02:05, Namespace wrote:
 I had never problems with that in C++.
 If I have members which are const because they are assigned only one
 time and needs no other assignment, why should I declare this member not
 as const?

 In the example I know exactly that I assign only one time a name to this
 struct, so why I should not declare it as const?

Perhaps declare "Name" as private and create a property getter for the field. I think this is a bit annoying as well, it worked in D1. -- /Jacob Carlborg
Sep 10 2012
parent Jacob Carlborg <doob me.com> writes:
On 2012-09-10 09:24, Jonathan M Davis wrote:

 And it works just fine in D2. It's const that's the problem. In general, if you
 want a member variable to be "read-only" on a struct, I'd strongly advise
 using a getter property without a setter property rather than making it const,
 because const makes it so that stuff like the assignment operator doesn't work.

Well, in D1 const was a nice shortcut for this. -- /Jacob Carlborg
Sep 10 2012
prev sibling parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 09/10/2012 02:49 AM, monarch_dodra wrote:
 On Sunday, 9 September 2012 at 23:54:45 UTC, Jonathan M Davis
 wrote:
 [SNIP]

 the default assignment operator illegal. You could overload it, and as
 long as
 it doesn't touch any of the const member variables, it would work, but
 the
 const member variable is stuck as it is, and anything trying to 


 illegal.

 [SNIP]

 - Jonathan M Davis

Not to that it is my goal to be a pain, but the example I provided *does* overload opAssign (and the CC), but it *doesn't* work. Notice the error message is: "Error: tests[4] isn't mutable" Which is simply not true. The default assignment operator, when trying to do an assignment creates: "Error: variable XXX cannot modify struct with immutable members." But that is not what we are seeing. It appears that when writting: tests[4] = Test("Foobar"); It *looks* like compiler is eliding the opAssign/CC completely, opting for a bit copy, which is illegal.

I don't see how opAssign is involved here. The problem is with the eager element insertion of the following expression alone: tests[4] That expression does add an element having the default value to the container. Try this program: import std.stdio; struct Test { string name; } Test alsoPrintLength(Test[uint] aa) { writefln("Adding to this AA: %s", aa); return Test(); } void main() { Test[uint] tests; tests[4] = alsoPrintLength(tests); } The output: Adding to this AA: [4:Test("")] As it demonstrates, the AA already has an element when alsoPrintLength() is called. The assignment in the last line of the program is assigning to that element which happens to have a const member in your example: struct Test { const string Name; this(string name) { this.Name = name; } } 'this.Name = name' fails to compile for that reason. Ali
Sep 11 2012
prev sibling next sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Saturday, 8 September 2012 at 23:18:14 UTC, Timon Gehr wrote:
 On 09/09/2012 01:16 AM, Namespace wrote:
 Why fail this code?
 without "const" on "Name" it works fine.

 http://dpaste.dzfl.pl/9fa0986a

const fields cannot be written to. This includes the case when the entire struct is written to at once.

That doesn't make sense to me either. Why would the entire "tests[4]" be immutable? If I were to define CC and opAssign, then why is there still a problem? Further more, changing from Associative Array to raw array solves the problem. This is a strange behavior for AAs. void main() { struct Test { const string Name; this(string name) { this.Name = name; } this(Test other) {} void opAssign(Test other) {} } Test[string] tests; Test t2 = Test("FooBar"); Test t3; t3 = t2; auto t4 = tests[4]; tests[4] = t4; }
Sep 09 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
On Saturday, 8 September 2012 at 23:18:14 UTC, Timon Gehr wrote:
 On 09/09/2012 01:16 AM, Namespace wrote:
 Why fail this code?
 without "const" on "Name" it works fine.

 http://dpaste.dzfl.pl/9fa0986a

const fields cannot be written to. This includes the case when the entire struct is written to at once.

But i assign the const attribute in the ctor. So this behaviour doesn't make any sense...
Sep 09 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, September 09, 2012 17:09:23 Namespace wrote:
 On Saturday, 8 September 2012 at 23:18:14 UTC, Timon Gehr wrote:
 On 09/09/2012 01:16 AM, Namespace wrote:
 Why fail this code?
 without "const" on "Name" it works fine.
 
 http://dpaste.dzfl.pl/9fa0986a

const fields cannot be written to. This includes the case when the entire struct is written to at once.

But i assign the const attribute in the ctor. So this behaviour doesn't make any sense...

const member variables can be initialized but never assigned to. They're either directly initialized or initialized by the constructor. After that (including later in the constructor), you can't change them. This also makes the default assignment operator illegal. You could overload it, and as long as it doesn't touch any of the const member variables, it would work, but the const member variable is stuck as it is, and anything trying to mutate is illegal. Really, having const member variables is a _bad_ idea IMHO - particularly for structs. At least with a class, if you try and replace the object's value, you generally just allocate a new object and assign it to the reference. But with structs, which are almost always on the stack, you can't assign to it anymore once it has a const member variable, which causes all kinds of annoying problems. You're just better off if you never declare structs with const or immutable member variables. - Jonathan M Davis
Sep 09 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
I had never problems with that in C++.
If I have members which are const because they are assigned only 
one time and needs no other assignment, why should I declare this 
member not as const?

In the example I know exactly that I assign only one time a name 
to this struct, so why I should not declare it as const?

Other example: you have a unique birthday date. This is const you 
cannot change it like a name or a telephone number. So if you 
have a Person struct which holds any data of a single people, you 
won't declare the date as const? I would. And I had expected that 
this is normal behaviour.
Sep 09 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, September 10, 2012 02:05:08 Namespace wrote:
 I had never problems with that in C++.
 If I have members which are const because they are assigned only
 one time and needs no other assignment, why should I declare this
 member not as const?
 
 In the example I know exactly that I assign only one time a name
 to this struct, so why I should not declare it as const?
 
 Other example: you have a unique birthday date. This is const you
 cannot change it like a name or a telephone number. So if you
 have a Person struct which holds any data of a single people, you
 won't declare the date as const? I would. And I had expected that
 this is normal behaviour.

Once something is const, you _cannot_ change it. C++ lets you cast away const and mutate things, meaning that they aren't really const. D doesn't allow that. Casting away const and mutating a variable is undefined behavior. D has no mutable keyword. D's const is also transitive, meaning that once something is const, everything within it is const (e.g. once you declare a container const, _every_ element inside it is const as well). So, any and all operations which would involve mutating a const variable are illegal - including mutating something which a const variable refers to, since _everything_ it refers to is const. With structs, this has the effect that once one member variable is const, you can never again reassign the whole thing. You can assign to its non-const members but not the whole thing. In this particular case, you're dealing with an AA. Depending on the AA's implementation, it should be possible to initialize tests[4] and then never assign to it again, but that's problematic due to rehashing (hash table's _need_ to be able to move objects around), and the current AA implementation is fairly poor anyway. So, it ends up default-initializing the value and _then_ assigning it, which doesn't work if you can't reassign the value. It shouldn't be doing that. It's a bug, and it causes problems with stuff like exceptions. For instance, if you have aa[key] = func(); and func throws, then aa ends up with a default-initialized value at key: http://d.puremagic.com/issues/show_bug.cgi?id=3825 druntime's AA implementation needs a fair bit of work. H.S. Teoh is working on a better implementation, but who knows when we'll have it. We just have to put up with buggy corner cases until then unfortunately (though at least AA's work just fine most of the time). I don't know if this particular use case will ever work though, because it makes it so that the struct can never be reassigned, and I'm not sure that it's at all reasonable for an AA to never be able to reassign its values. Yes, initialization should work (unlike now), but rehashing needs to move values around, and hash tables have to be able to rehash when they get full enough, meaning that being able to reassign elements will probably be required. - Jonathan M Davis
Sep 09 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, September 10, 2012 09:13:04 Jacob Carlborg wrote:
 On 2012-09-10 02:05, Namespace wrote:
 I had never problems with that in C++.
 If I have members which are const because they are assigned only one
 time and needs no other assignment, why should I declare this member not
 as const?
 
 In the example I know exactly that I assign only one time a name to this
 struct, so why I should not declare it as const?

Perhaps declare "Name" as private and create a property getter for the field. I think this is a bit annoying as well, it worked in D1.

And it works just fine in D2. It's const that's the problem. In general, if you want a member variable to be "read-only" on a struct, I'd strongly advise using a getter property without a setter property rather than making it const, because const makes it so that stuff like the assignment operator doesn't work. - Jonathan M Davis
Sep 10 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, September 10, 2012 09:30:59 Jacob Carlborg wrote:
 On 2012-09-10 09:24, Jonathan M Davis wrote:
 And it works just fine in D2. It's const that's the problem. In general,
 if you want a member variable to be "read-only" on a struct, I'd strongly
 advise using a getter property without a setter property rather than
 making it const, because const makes it so that stuff like the assignment
 operator doesn't work.


Whereas in D2, const is both something of fantastic wonder and utter despair. :) - Jonathan M Davis
Sep 10 2012
prev sibling next sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Sunday, 9 September 2012 at 23:54:45 UTC, Jonathan M Davis
wrote:
 [SNIP]

 the default assignment operator illegal. You could overload it, 
 and as long as
 it doesn't touch any of the const member variables, it would 
 work, but the
 const member variable is stuck as it is, and anything trying to 
 mutate is
 illegal.

 [SNIP]

 - Jonathan M Davis

Not to that it is my goal to be a pain, but the example I provided *does* overload opAssign (and the CC), but it *doesn't* work. Notice the error message is: "Error: tests[4] isn't mutable" Which is simply not true. The default assignment operator, when trying to do an assignment creates: "Error: variable XXX cannot modify struct with immutable members." But that is not what we are seeing. It appears that when writting: tests[4] = Test("Foobar"); It *looks* like compiler is eliding the opAssign/CC completely, opting for a bit copy, which is illegal.
Sep 10 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, September 10, 2012 11:49:48 monarch_dodra wrote:
 It appears that when writting:
 tests[4] = Test("Foobar");
 It *looks* like compiler is eliding the opAssign/CC completely,
 opting for a bit copy, which is illegal.

As I believe was mentioned elsewhere in this thread, that's due to http://d.puremagic.com/issues/show_bug.cgi?id=6906 AA's work fine for the most part, but they're really quite buggy when it comes to corner cases. A new implementation is being worked on, but it's slow in coming. - Jonathan M Davis
Sep 10 2012
prev sibling next sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Monday, 10 September 2012 at 09:54:46 UTC, Jonathan M Davis 
wrote:
 On Monday, September 10, 2012 11:49:48 monarch_dodra wrote:
 It appears that when writting:
 tests[4] = Test("Foobar");
 It *looks* like compiler is eliding the opAssign/CC completely,
 opting for a bit copy, which is illegal.

As I believe was mentioned elsewhere in this thread, that's due to http://d.puremagic.com/issues/show_bug.cgi?id=6906 AA's work fine for the most part, but they're really quite buggy when it comes to corner cases. A new implementation is being worked on, but it's slow in coming. - Jonathan M Davis

Oops, sorry. TY.
Sep 10 2012
prev sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Tuesday, 11 September 2012 at 09:00:15 UTC, Ali Çehreli wrote:
 On 09/10/2012 02:49 AM, monarch_dodra wrote:
 On Sunday, 9 September 2012 at 23:54:45 UTC, Jonathan M Davis
 wrote:
 [SNIP]

 the default assignment operator illegal. You could overload


 long as
 it doesn't touch any of the const member variables, it would


 the
 const member variable is stuck as it is, and anything trying


 illegal.

 [SNIP]

 - Jonathan M Davis

Not to that it is my goal to be a pain, but the example I provided *does* overload opAssign (and the CC), but it

 work.

 Notice the error message is:
 "Error: tests[4] isn't mutable"
 Which is simply not true.

 The default assignment operator, when trying to do an

 "Error: variable XXX cannot modify struct with immutable

 But that is not what we are seeing.

 It appears that when writting:
 tests[4] = Test("Foobar");
 It *looks* like compiler is eliding the opAssign/CC

 opting for a bit copy, which is illegal.

I don't see how opAssign is involved here. The problem is with the eager element insertion of the following expression alone: tests[4] That expression does add an element having the default value to the container.

No arguments there. tests[4] first creates an element initialized to the value of Test.init.
 [SNIP]

 'this.Name = name' fails to compile for that reason.

 Ali

The difference is that the opAssign operator I defined specifically does not attempt to modify the const member, so assigning to an already existing object is legal. Even with that though, I have: void main() { Test t1; Test[4] testsArray; Test[int] tests; t1 = Test("hello"); // (1) testsArray[3] = Test("hello"); // (2) tests[4] = Test("hello"); // (3) } In this program: (1) compiles and is perfectly legal. (2) ditto. (3) This fails to compile, but it has no reason not to.
Sep 11 2012