www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - What guarantees does D 'const' provide, compared to C++?

reply "Mehrdad" <wfunction hotmail.com> writes:
Something I'm having trouble undertanding/remembering (sorry, 
you've probaby already explained it a billion times)...

I remember being told many times that D's 'const' provides 
stronger guarantees than C++'s 'const'.

I just wanted to clarify, is that true for 'const' itself, or is 
that referring only to when 'const' is used with other D-specific 
features (immutable, etc.)?

If it's the former, is there some example piece of code in each 
language, for comparison, that shows how the compiler can infer 
more from D's const than C++'s?

If so, it might be worth posting something like that on the 
website.
Aug 16 2012
next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
 I remember being told many times that D's 'const' provides 
 stronger guarantees than C++'s 'const'.

I also remember being told that the compiler considers it UB to cast away const-ness in references, unlike in C++, which gives you more guarantees. But I'm having trouble coming up with a pieces of source code that illustrate the issue.
Aug 16 2012
prev sibling next sibling parent reply "Jesse Phillips" <Jessekphillips+D gmail.com> writes:
On Thursday, 16 August 2012 at 22:14:31 UTC, Mehrdad wrote:
 Something I'm having trouble undertanding/remembering (sorry, 
 you've probaby already explained it a billion times)...

 I remember being told many times that D's 'const' provides 
 stronger guarantees than C++'s 'const'.

 If it's the former, is there some example piece of code in each 
 language, for comparison, that shows how the compiler can infer 
 more from D's const than C++'s?

Note that stronger guarentees does not translate to inferences done by the compiler.
 If so, it might be worth posting something like that on the 
 website.

I believe there is an article which speaks to const, but the inference benefits come from immutability and pure functions. The const page does have a comparison table at the end: http://dlang.org/const3.html The main thing given is transitivity. The compiler will guarantee you aren't changing data that is const. On the note of casting away const, I don't believe that is the operation which is undefined, however modifying const is undefined as it could be pointing to immutable data.
Aug 16 2012
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 8/16/2012 6:43 PM, Mehrdad wrote:
 On Friday, 17 August 2012 at 01:25:18 UTC, Chris Cain wrote:
 Yeah. Again, you can't modify __the const view__.

Isn't that kinda useless, if it tells you nothing about the object itself?

It means you can write code that can process both mutable and immutable objects.
Aug 17 2012
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 8/17/2012 2:34 PM, Mehrdad wrote:
 On Friday, 17 August 2012 at 20:46:02 UTC, Walter Bright wrote:
 On 8/16/2012 6:43 PM, Mehrdad wrote:
 On Friday, 17 August 2012 at 01:25:18 UTC, Chris Cain wrote:
 Yeah. Again, you can't modify __the const view__.

Isn't that kinda useless, if it tells you nothing about the object itself?

It means you can write code that can process both mutable and immutable objects.

I meant from a C++/D comparison standpoint...

I don't know what you're driving at.
Aug 17 2012
parent Walter Bright <newshound2 digitalmars.com> writes:
On 8/18/2012 4:28 PM, Mehrdad wrote:
 Right, most optimizations are not applicable to general cases.

That's pretty much always true with optimizations.
Aug 18 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
On Thursday, 16 August 2012 at 23:18:08 UTC, Jesse Phillips wrote:
 Note that stronger guarentees does not translate to inferences 
 done by the compiler.

I remember being told (correct me if I'm wrong) that D's const lets the compiler perform better optimizations than C++, which i.e. means the compiler can infer more about the source code.
 The const page does have a comparison table at the end:

 http://dlang.org/const3.html

That just tells you how to use const, not why it's useful compared to C++.
 The main thing given is transitivity.

Sure, but what kind of an advantage does that provide compared to C++? (As in, a code sample would be awesome -- it's so much easier to explain with an example to compare, rather than with words.)
 The compiler will guarantee you aren't changing data that is 
 const.

Right, but it's the same with C++, right? Where's the difference?
 On the note of casting away const, I don't believe that is the 
 operation which is undefined, however modifying const is 
 undefined as it could be pointing to immutable data.

Oh, then that's not what I'd understood. Seems just like C++ then.
 I believe there is an article which speaks to const, but the 
 inference benefits come from immutability and pure functions.

Ahh, right. You need immutability and purity for const to be any more advantageous than in C++. It seems like it's a point worth emphasizing in the const article (and perhaps when explaining const to newbies), since people might not understand why const is powerful unless they actually start using immutability, too. (Also kind of prevents the situation where C++ users try to use const as logical const, without realizing that it's meant to work with immutability/purity instead.)
Aug 16 2012
prev sibling next sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Friday, August 17, 2012 01:35:46 Mehrdad wrote:
 The main thing given is transitivity.

Sure, but what kind of an advantage does that provide compared to C++? (As in, a code sample would be awesome -- it's so much easier to explain with an example to compare, rather than with words.)

In C++, if you have const vector<T*>& getStuff() const; which returns a member variable, then as long as const isn't cast away, you know that the container itself won't have any elements added or removed from it, but you have _zero_ guarantees about the elements themselves. In contrast, in D, const ref Array!(T*) getStuff() const; you would _know_ that not only is the container not altered, but you know that the elements aren't altered either - or anything which the elements point to. And since casting away const and mutating a variable is undefined behavior, and there is no mutable, you don't have to worry about the state of the object changing at all unless it's shared (since another thread could alter it through a non-const reference) or you can somehow get another, non-const reference to it in the current thread. So, it becomes trivial for a class or struct to guarantee that nothing can change the member variables that it returns from const functions.
 The compiler will guarantee you aren't changing data that is
 const.

Right, but it's the same with C++, right? Where's the difference?

C++ makes no such guarantees, because you're free to cast away const and modifiy objects and because objects can have members marked with the mutable keyword. All const really does in C++ is make it so that you don't accidentally, directly modify a variable which is const. It makes _zero_ guarantees about the state of the object beyond that.
 On the note of casting away const, I don't believe that is the
 operation which is undefined, however modifying const is
 undefined as it could be pointing to immutable data.

Oh, then that's not what I'd understood. Seems just like C++ then.

No. Casting away const and modifying an object is well-defined in C++. The casting is well-defined in D but not the modification. That's undefined behavior - both because it will do nasty things if the object is actually immutable and because D allows the compiler to assume that anything which is const is not modified by another thread if it's not shared (the _only_ thing which could alter that variable is another, mutable reference to the same data on the same thread).
 I believe there is an article which speaks to const, but the
 inference benefits come from immutability and pure functions.

Ahh, right. You need immutability and purity for const to be any more advantageous than in C++. It seems like it's a point worth emphasizing in the const article (and perhaps when explaining const to newbies), since people might not understand why const is powerful unless they actually start using immutability, too. (Also kind of prevents the situation where C++ users try to use const as logical const, without realizing that it's meant to work with immutability/purity instead.)

const is useful even without purity, because you _know_ that nothing which is const can change unless you access it through a non-const reference, and everything being thread-local by default makes it that much harder for that to happen, allowing the compiler to more easily guarantee that a const object isn't changed and do optimizations based on that. Now, it _does_ get far better once purity is added into the mix, because then neither you or the compiler has to worry about function calls altering a variable through global variables, since they can't be accessed, making it easier for you to understand the code and easier for the compiler to make optimizations. But D's const by itself still allows for more optimizations than C++'s const does. And of course, immutable offers even better guarantees, since neither you nor the compiler has to worry about there being mutable references to the same data anywhere. So, it enables even better optimizations (and combined with pure, it can even lead to whole function calls being elided). While D's const can be too restrictive at times, I'm increasingly annoyed by how flimsy C++'s const is in comparison. In particular, without transitivity, putting const on a getter frequently means next to nothing. - Jonathan M Davis
Aug 16 2012
parent Walter Bright <newshound2 digitalmars.com> writes:
On 8/16/2012 5:51 PM, Torarin wrote:
 The C++ standard, section 7.1.6.1:

 Except that any class member declared mutable (7.1.1) can be modified,
 any attempt to modify a const
   object during its lifetime (3.8) results in undefined behavior.

That applies to a "const object", i.e. an object declared as const: const int x; It does not apply to a non-const object. If you've got a pointer-to-const, and it points to a non-const object, you can legitimately cast away the const-ness, and modify it. Such is not undefined behavior. Check 5.2.11. The crux of the matter is C++'s use of the word "const" to sometimes mean a storage class and sometimes a type constructor, and it takes some careful reading of the spec to pick apart the two. In D, const is always a type constructor, and casting away const and then modifying the resulting object is undefined behavior, whether or not the original object was const or mutable.
Aug 17 2012
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Mehrdad:

 On the note of casting away const, I don't believe that is the 
 operation which is undefined, however modifying const is 
 undefined as it could be pointing to immutable data.

Oh, then that's not what I'd understood. Seems just like C++ then.

Are you sure? bye, bearophile
Aug 16 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
On Friday, 17 August 2012 at 00:10:52 UTC, Jonathan M Davis wrote:
 In C++, if you have

 const vector<T*>& getStuff() const;

 which returns a member variable, then as long as const isn't 
 cast away, you know that the container itself won't have any 
 elements added or removed from it, but you have _zero_ 
 guarantees about the elements themselves.

Right.
 In contrast, in D,

 const ref Array!(T*) getStuff() const;

 you would _know_ that not only is the container not altered,
 but you know that the elements aren't altered either -
 or anything which the elements point to.

I'm not so sure about that. int yourGlobalCounter; struct MyIntPtrArray { int*[] items; MyIntPtrArray() { items = new int*[1]; items[0] = &yourGlobalField; } ref const(int*[]) getItems() const { ++yourGlobalCounter; return items; } }
 And since casting away const and mutating a variable is 
 undefined behavior,

That is either vague, or contradicts what I was told above. We all know casting away a const _object_ is undefined behavior in both D and C++. However, is casting away a const __reference__ undefined behavior in D? (It's not in C++.)
 The compiler will guarantee you aren't changing data that is 
 const.

Right, but it's the same with C++, right? Where's the difference?

C++ makes no such guarantees, because you're free to cast away const and modifiy objects

Not correct, as far as I understand. C++ only lets you cast away _const references_ to _mutable_ objects. If the object happens to have been const _itself_, then that's undefined behavior.
 and because objects can have members marked with the mutable 
 keyword.

Right, it's the equivalent of static fields, see my example above.
 On the note of casting away const, I don't believe that is 
 the operation which is undefined, however modifying const is 
 undefined as it could be pointing to immutable data.

then.

No. Casting away const and modifying an object is well-defined in C++.

Only if the object was mutable to begin with. See above.
 const is useful even without purity, because you _know_ that 
 nothing which is const can change unless you access it through 
 a non-const reference

Yeah, and as I just showed, aliasing messes this up just as badly as in C++. See above.
 Now, it _does_ get far better once purity is added into the mix
 And of course, immutable offers even better guarantees

Yup, I agree about those; I'm just referring to const here.
Aug 16 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
On Friday, 17 August 2012 at 00:32:03 UTC, Mehrdad wrote:
 Not correct, as far as I understand.
 C++ only lets you cast away _const references_ to _mutable_ 
 objects.
 If the object happens to have been const _itself_, then that's 
 undefined behavior.

Er, minor correction: By "casting away const" I mean "casting away const and actually mutating the object through the new, unconst reference". Obviously, I'm not referring to the mere cast itself, which doesn't do anything unless used for something else.
Aug 16 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
On Friday, 17 August 2012 at 00:13:58 UTC, bearophile wrote:
 Mehrdad:

 On the note of casting away const, I don't believe that is 
 the operation which is undefined, however modifying const is 
 undefined as it could be pointing to immutable data.

Oh, then that's not what I'd understood. Seems just like C++ then.

Are you sure?

Yes, I'm pretty sure that something like const int x = 5; int* p = const_cast<int*>(&x); *p = 6; is undefined behavior, because x is a const object. However, int y = 5; const int& x = y; int* p = const_cast<int*>(&x); *p = 6; is well-defined, because the object is mutable. At least, that's my understanding of C++; feel free to correct me if I'm wrong.
Aug 16 2012
prev sibling next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Friday, August 17, 2012 02:32:01 Mehrdad wrote:
 C++ makes no such guarantees, because you're free to cast away
 const and modifiy objects

Not correct, as far as I understand. C++ only lets you cast away _const references_ to _mutable_ objects. If the object happens to have been const _itself_, then that's undefined behavior.

Well, then our understandings on the matter conflict, and I don't know for certain which of us is correct. - Jonathan M Davis
Aug 16 2012
prev sibling next sibling parent "Chris Cain" <clcain uncg.edu> writes:
On Friday, 17 August 2012 at 00:32:03 UTC, Mehrdad wrote:
 On Friday, 17 August 2012 at 00:10:52 UTC, Jonathan M Davis 
 wrote:
 In contrast, in D,

 const ref Array!(T*) getStuff() const;

 you would _know_ that not only is the container not altered,
 but you know that the elements aren't altered either -
 or anything which the elements point to.

I'm not so sure about that. ...snip...

Your example is not equivalent to what he was saying. Also, D's const is _not_ a guarantee that there are no mutable references to something. That'd be immutable. It just says that it's illegal to modify something which is const (... i.e. _directly_ ... obviously, you can modify something which is const via a mutable reference dangling around, but that's _not_ something that const is supposed to protect you from)
 Right, it's the equivalent of static fields, see my example 
 above.

ditto to above
Aug 16 2012
prev sibling next sibling parent Torarin <torarind gmail.com> writes:
2012/8/17 Jonathan M Davis <jmdavisProg gmx.com>
 On Friday, August 17, 2012 02:32:01 Mehrdad wrote:
 C++ makes no such guarantees, because you're free to cast away
 const and modifiy objects

Not correct, as far as I understand. C++ only lets you cast away _const references_ to _mutable_ objects. If the object happens to have been const _itself_, then that's undefined behavior.

Well, then our understandings on the matter conflict, and I don't know for certain which of us is correct. - Jonathan M Davis

The C++ standard, section 7.1.6.1: Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior. Torarin
Aug 16 2012
prev sibling next sibling parent "Chris Cain" <clcain uncg.edu> writes:
On Friday, 17 August 2012 at 00:44:20 UTC, Chris Cain wrote:
 Also, D's const is _not_ a guarantee that there are no mutable 
 references to something. That'd be immutable.

And, by the way, I'd call this a bug (not sure if reported yet): int yourGlobalCounter; struct S { int*[] items; this(int i) { items = new int*[1]; items[0] = &yourGlobalCounter; yourGlobalCounter = i; } ref const(int*[]) getItems() const { ++yourGlobalCounter; return items; } } import std.stdio; void main() { immutable(S) s = immutable(S)(0); auto it = s.getItems(); writeln(*it[0]); s.getItems(); writeln(*it[0]); }
Aug 16 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
On Friday, 17 August 2012 at 00:44:20 UTC, Chris Cain wrote:
 On Friday, 17 August 2012 at 00:32:03 UTC, Mehrdad wrote:
 On Friday, 17 August 2012 at 00:10:52 UTC, Jonathan M Davis 
 wrote:
 In contrast, in D,

 const ref Array!(T*) getStuff() const;

 you would _know_ that not only is the container not altered,
 but you know that the elements aren't altered either -
 or anything which the elements point to.

I'm not so sure about that. ...snip...

Your example is not equivalent to what he was saying.

How? Jon said "you know that the elements aren't altered either - or anything which the elements point to". I just showed that the const-ness of getStuff() tells you _nothing_ about that fact. Did I miss something?
Aug 16 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
On Friday, 17 August 2012 at 00:51:55 UTC, Torarin wrote:
 The C++ standard, section 7.1.6.1:

 Except that any class member declared mutable (7.1.1) can be 
 modified, any attempt to modify a const object during its 
 lifetime (3.8) results in undefined behavior.

 Torarin

+1 thanks for taking the time to look it up. :)
Aug 16 2012
prev sibling next sibling parent "Chris Cain" <clcain uncg.edu> writes:
On Friday, 17 August 2012 at 01:17:19 UTC, Mehrdad wrote:
 How?


 Jon said "you know that the elements aren't altered either - or 
 anything which the elements point to".


 I just showed that the const-ness of getStuff() tells you 
 _nothing_ about that fact.

 Did I miss something?

Yeah. Again, you can't modify __the const view__. It's legal for const things to be mutable underneath. And it's legal to modify mutable things via mutable views. Ergo, const things can change that way. But that's not what he's trying to tell you. If you want something to be guaranteed to never change through any means, you use immutable. And if you find a way to mutate immutable things (without casts), it's a bug, not a feature (and thus should be reported). I'm pretty sure everyone's aware of the inconsistency with immutable and the constructors at this time (like the bug I pointed out above), so that's kind of a weak point where the programmer actually has to know what they're doing, but I'm sure that'll be fixed at some point.
Aug 16 2012
prev sibling next sibling parent "Chris Cain" <clcain uncg.edu> writes:
Just to be abundantly clear about his point.

// v is a const vector, but holds pointers ...

*v[0] = 5; // legal in C++, illegal in D (except in constructors 
which will allow this, ATM)

Transitivity gives you more information and guarantees about what 
you can and can't do with your view. Also, if the only view of 
the data you have is that const view, it's effectively the same 
as immutable (it couldn't be changed by any valid code).
Aug 16 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
On Friday, 17 August 2012 at 01:25:18 UTC, Chris Cain wrote:
 Yeah. Again, you can't modify __the const view__.

Isn't that kinda useless, if it tells you nothing about the object itself?
Aug 16 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
Also note that Jon __clearly__ said:

"you know that the elements aren't altered either anything which 
the elements point to".



He was clearly _not_ talking about modifying the pointer.
He said you cannot alter the "elements pointed TO".


Given that, I have no idea how that is supposed to be saying "you 
can't modify the const _view_". He's clearly talking about the 
target of the pointer, not the pointer itself.
Aug 16 2012
prev sibling next sibling parent "Chris Cain" <clcain uncg.edu> writes:
On Friday, 17 August 2012 at 01:43:03 UTC, Mehrdad wrote:
 Isn't that kinda useless, if it tells you nothing about the 
 object itself?

Not sure what your point is. It tells you enough about how you work with that "object itself" and sets (real) boundaries which is unlike C++'s const which tells you truly nothing. Seems pretty useful in my eyes. C++'s const is the thing that's really useless. It's an abstraction, and like all abstractions, you don't necessarily get a perfect understanding of the bits and bytes and where they are and how they're going to function. But it does give you more reasoning ability about your code if used properly. Plus don't forget that it allows you to use the same code for immutable and mutable objects, which is extremely valuable.
Aug 16 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
On Friday, 17 August 2012 at 01:33:29 UTC, Chris Cain wrote:
 Also, if the only view of the data you have is that const view, 
 it's effectively the same as immutable (it couldn't be changed 
 by any valid code).

So you're saying casting away a const _pointer_ is undefined, even if the target was originally created as mutable. (Otherwise, the code would certainly be "valid", just like in C++.) Which means you can effectively _never_ cast away constness of a pointer/reference, no matter how certain you are about the target object, right? If you did, then the code would be invalid, and the compiler could simply format your C: drive instead of modifying the object. If so, then why is such an undefined cast allowed in the first place?
Aug 16 2012
prev sibling next sibling parent "Chris Cain" <clcain uncg.edu> writes:
On Friday, 17 August 2012 at 01:45:27 UTC, Mehrdad wrote:
 He was clearly _not_ talking about modifying the pointer.
 He said you cannot alter the "elements pointed TO".


 Given that, I have no idea how that is supposed to be saying 
 "you can't modify the const _view_". He's clearly talking about 
 the target of the pointer, not the pointer itself.

I'll let Mr. Davis confirm which he was talking about. The only thing that's clear is that our understandings of his point differ.
Aug 16 2012
prev sibling next sibling parent "Chris Cain" <clcain uncg.edu> writes:
On Friday, 17 August 2012 at 01:51:38 UTC, Mehrdad wrote:
 So you're saying casting away a const _pointer_ is undefined, 
 even if the target was originally created as mutable. 
 (Otherwise, the code would certainly be "valid", just like in 
 C++.)


 Which means you can effectively _never_ cast away constness of 
 a pointer/reference, no matter how certain you are about the 
 target object, right?

 If you did, then the code would be invalid, and the compiler 
 could simply format your C: drive instead of modifying the 
 object.

 If so, then why is such an undefined cast allowed in the first 
 place?

If you're absolutely 100% completely totally certain that the data is mutable (i.e., you have confirmed either through good, sound reasoning OR you have some method of seeing exactly where it is stored in your RAM and you've checked that the place it is stored is writable memory), then __technically__, yes you can cast away and modify away. Apparently, according to some, it's necessary for low level programming. I'd highly discourage this type of behavior because if you're doing something like that I'm nearly certain you could come up with a better design, not to mention you're missing the point of having something const in the first place. Let's just say I wouldn't use const in a place where I'm itching to cast it away. If you're not going to use it to your advantage, there's no point.
Aug 16 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
On Friday, 17 August 2012 at 01:50:02 UTC, Chris Cain wrote:
 On Friday, 17 August 2012 at 01:43:03 UTC, Mehrdad wrote:
 Isn't that kinda useless, if it tells you nothing about the 
 object itself?

Not sure what your point is. It tells you enough about how you work with that "object itself"

Are you sure? struct MyStruct { static int* x; int y; this() { } this(int* z) { x = z; } auto getValue() const { ++*x; return this.y; } } auto s = MyStruct(); s = MyStruct(&s.y); s.getValue(); // const, but returns 1 s.getValue(); // const, but returns 2 s.getValue(); // const, but returns 3 So, effectively, the mere const-ness (and even its transitivity) is useless to the compiler.
 I'll let Mr. Davis confirm which he was talking about. The only 
 thing that's clear is that our understandings of his point 
 differ.

Okay sure, thanks for trying to explain it anyway!
Aug 16 2012
prev sibling next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Friday, August 17, 2012 03:52:38 Chris Cain wrote:
 On Friday, 17 August 2012 at 01:45:27 UTC, Mehrdad wrote:
 He was clearly _not_ talking about modifying the pointer.
 He said you cannot alter the "elements pointed TO".
 
 
 Given that, I have no idea how that is supposed to be saying
 "you can't modify the const _view_". He's clearly talking about
 the target of the pointer, not the pointer itself.

I'll let Mr. Davis confirm which he was talking about. The only thing that's clear is that our understandings of his point differ.

If you have a const object, then you have the guarantee that none of what it contains or refers to either directly or indirectly can be altered through that reference or pointer. It's possible to alter it via other references, and without pure, const functions called on that object may still be able to alter by using global variables, but you do have the guarantee that that object and anything and everything that it refers to won't be directly mutated by that reference. - Jonathan M Davis
Aug 16 2012
prev sibling next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Friday, August 17, 2012 03:51:36 Mehrdad wrote:
 On Friday, 17 August 2012 at 01:33:29 UTC, Chris Cain wrote:
 Also, if the only view of the data you have is that const view,
 it's effectively the same as immutable (it couldn't be changed
 by any valid code).

So you're saying casting away a const _pointer_ is undefined, even if the target was originally created as mutable. (Otherwise, the code would certainly be "valid", just like in C++.) Which means you can effectively _never_ cast away constness of a pointer/reference, no matter how certain you are about the target object, right? If you did, then the code would be invalid, and the compiler could simply format your C: drive instead of modifying the object. If so, then why is such an undefined cast allowed in the first place?

Because there are plenty of functions which take mutable objects but don't actually alter them - particularly when interacting with C code. It's the sort of thing that you really shouldn't be doing normally but on rare occasions is useful - in this case when dealing with code that isn't const correct but still won't actually alter the object. - Jonathan M Davis
Aug 16 2012
prev sibling next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Friday, August 17, 2012 03:00:34 Chris Cain wrote:
 On Friday, 17 August 2012 at 00:44:20 UTC, Chris Cain wrote:
 Also, D's const is _not_ a guarantee that there are no mutable
 references to something. That'd be immutable.

And, by the way, I'd call this a bug (not sure if reported yet): int yourGlobalCounter; struct S { int*[] items; this(int i) { items = new int*[1]; items[0] = &yourGlobalCounter; yourGlobalCounter = i; } ref const(int*[]) getItems() const { ++yourGlobalCounter; return items; } } import std.stdio; void main() { immutable(S) s = immutable(S)(0); auto it = s.getItems(); writeln(*it[0]); s.getItems(); writeln(*it[0]); }

How is it a bug? The variable that you're altering is not part of the object. That's part of why having pure with const in so valuable. It prevents stuff like what you're doing here. - Jonathan M Davis
Aug 16 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
On Friday, 17 August 2012 at 01:59:03 UTC, Chris Cain wrote:
 On Friday, 17 August 2012 at 01:51:38 UTC, Mehrdad wrote:
 So you're saying casting away a const _pointer_ is undefined, 
 even if the target was originally created as mutable. 
 (Otherwise, the code would certainly be "valid", just like in 
 C++.)


 Which means you can effectively _never_ cast away constness of 
 a pointer/reference, no matter how certain you are about the 
 target object, right?

 If you did, then the code would be invalid, and the compiler 
 could simply format your C: drive instead of modifying the 
 object.

 If so, then why is such an undefined cast allowed in the first 
 place?

If you're absolutely 100% completely totally certain that the data is mutable (i.e., you have confirmed either through good, sound reasoning OR you have some method of seeing exactly where it is stored in your RAM and you've checked that the place it is stored is writable memory), then __technically__, yes you can cast away and modify away.

If doing so is "invalid" (i.e. undefined?) like you mentioned, no, you can't, because the compiler can't guarantee it can respect your assumptions and generate otherwise-correct code. If it IS valid, then it seems to me like the situation is the exact same with C++.
Aug 16 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
On Friday, 17 August 2012 at 02:02:56 UTC, Jonathan M Davis wrote:
 How is it a bug? The variable that you're altering is not part 
 of the object.

It doesn't need to be. What you said was:
 If you have a const object, then you have the guarantee that 
 none of what it contains or refers to ____either directly or 
 indirectly_____ can be altered through that reference or 
 pointer.

But as I just showed, it can, so... yeah.
 That's part of why having pure with const in so valuable.

Right, but purity is another topic. :)
Aug 16 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
On Friday, 17 August 2012 at 02:02:51 UTC, Jonathan M Davis wrote:
 Because there are plenty of functions which take mutable 
 objects but don't actually alter them - particularly when 
 interacting with C code.

Ah, so that explains that, thanks. So to clarify, modifying a mutable object through casting away a const reference is undefined in D, but valid in C++. Now the only question is what guarantees that actually gives you that the compiler can use for optimization (aliasing issue above^).
Aug 16 2012
prev sibling next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Friday, August 17, 2012 02:32:01 Mehrdad wrote:
 On Friday, 17 August 2012 at 00:10:52 UTC, Jonathan M Davis wrote:
 In C++, if you have
 
 const vector<T*>& getStuff() const;
 
 which returns a member variable, then as long as const isn't
 cast away, you know that the container itself won't have any
 elements added or removed from it, but you have _zero_
 guarantees about the elements themselves.

Right.
 In contrast, in D,
 
 const ref Array!(T*) getStuff() const;
 
 you would _know_ that not only is the container not altered,
 but you know that the elements aren't altered either -
 or anything which the elements point to.

I'm not so sure about that. int yourGlobalCounter; struct MyIntPtrArray { int*[] items; MyIntPtrArray() { items = new int*[1]; items[0] = &yourGlobalField; } ref const(int*[]) getItems() const { ++yourGlobalCounter; return items; } }

What I meant is that you know that nothing was altered through the reference that the getter returned. You don't have any such guarantee in C++. But even if both the constructor and getItems are pure, you could still pass the initial value of items in as an argument to the constructor making it so that there are potentially references to it outside MyIntPtrArray, which is why immutable buys you so much. So, the optimizations permitted by const alone (and to some extent even with pure) do tend to be fairly localized precisely because of the number of ways that the const variable could refer to data which is altered by other, mutable references to it. - Jonathan M Davis
Aug 16 2012
prev sibling next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Friday, August 17, 2012 03:59:01 Chris Cain wrote:
 If you're absolutely 100% completely totally certain that the
 data is mutable (i.e., you have confirmed either through good,
 sound reasoning OR you have some method of seeing exactly where
 it is stored in your RAM and you've checked that the place it is
 stored is writable memory), then __technically__, yes you can
 cast away and modify away. Apparently, according to some, it's
 necessary for low level programming. I'd highly discourage this
 type of behavior because if you're doing something like that I'm
 nearly certain you could come up with a better design, not to
 mention you're missing the point of having something const in the
 first place.

It's still technically undefined behavior. It's just that there's pretty much no way that the compiler is going to be written in a way that it won't work. However, you _do_ still risk running into problems because the compiler may make optimizations that you're violating by modifying the object. So, even if you're _certain_ that the object is actually mutable and you're _certain_ that nothing will blow up when you cast away const and modify it, you _still_ could get bugs due to compiler optimizations. - Jonathan M Davis
Aug 16 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
On Friday, 17 August 2012 at 02:09:09 UTC, Mehrdad wrote:
 On Friday, 17 August 2012 at 02:02:56 UTC, Jonathan M Davis 
 wrote:
 How is it a bug? The variable that you're altering is not part 
 of the object.

It doesn't need to be.

Not to say it /can't/ be, of course... a const method can certainly legally modify its own object's members. All you need is some aliasing. http://forum.dlang.org/thread/gjccnstbihxbrwhwwucs forum.dlang.org?page=3#post-mailman.1286.1345168975.31962.digitalmars-d:40puremagic.com
Aug 16 2012
prev sibling next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Friday, August 17, 2012 04:12:10 Mehrdad wrote:
 On Friday, 17 August 2012 at 02:02:51 UTC, Jonathan M Davis wrote:
 Because there are plenty of functions which take mutable
 objects but don't actually alter them - particularly when
 interacting with C code.

Ah, so that explains that, thanks. So to clarify, modifying a mutable object through casting away a const reference is undefined in D, but valid in C++. Now the only question is what guarantees that actually gives you that the compiler can use for optimization (aliasing issue above^).

Yeah. It's more than C++, but it's still pretty limited without pure, and if even with pure, the optimizations can still be pretty limited. Immutable gives a _lot_ more. And while there's definitely optimizations to  be had with const, I suspect that dmd doesn't exploit them very well at this point. Other issues have had far higher priority than eking out every last bit of performance that we can. So, regardless of what D's current performance is, I think that it's pretty much a guarantee that it will improve as D matures. - Jonathan M Davis
Aug 16 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
On Friday, 17 August 2012 at 02:25:22 UTC, Jonathan M Davis wrote:
 Yeah. It's more than C++, but it's still pretty limited without 
 pure, and if even with pure, the optimizations can still be 
 pretty limited.

Yeah, I'm only worried about the language here, not the implementation. On Friday, 17 August 2012 at 02:14:22 UTC, Jonathan M Davis wrote:
 What I meant is that you know that nothing was altered through 
 the reference that the getter returned. You don't have any such 
 guarantee in C++.

I'm not following.. It seems to contradict my second example: struct MyStruct { static int* x; int y; this() { } this(int* z) { x = z; } auto getValue() const { ++*x; return this.y; } } auto s = MyStruct(); s = MyStruct(&s.y); s.getValue(); // const, but returns 1 s.getValue(); // const, but returns 2 s.getValue(); // const, but returns 3 So unless you're expecting the compiler to have the implementation for the entire class available in order for it to be able to do any kind of optimization (in which case, it would have to do a whole bunch of inference to figure out the aliasing issues, which would amount to what a C++ could try do just as well), I'm not seeing where the additional guarantees/potential optimizations are.
Aug 16 2012
prev sibling next sibling parent "Chris Cain" <clcain uncg.edu> writes:
On Friday, 17 August 2012 at 02:01:11 UTC, Mehrdad wrote:
...snip...

 Are you sure?

I've already responded to something that is equivalent to what you just posted. I'm not sure if you're intentionally being obtuse (I'll give you the benefit of the doubt) or if your eyes are glossing over when you get to the important parts of my posts, but in either case I don't feel like repeating myself again. For your benefit, I'm repeating myself as clearly as I can: Const is basically a view of your data. If you have something that's const, it's like looking through a filter that only allows you to do certain things. It is _not_ a guarantee that it's the only view of your data that exists in the universe. Your code, again, shows that you have two views of your memory. x is a mutable view of your data, and you're mutating it. y is your const view of your data. No matter how hard you try (without casts), you can't mutate y. You _can_ mutate x, which y is viewing. The y view updates accordingly, but you didn't mutate y. Maybe in a second you'll say "oh, but that's mutating y" and I'll promptly ignore you completely this time because you've yet again missed the point:2 You mutated x, not y, y is a view on the same data that x is so mutating x would logically have y's view updated since const isn't a guarantee that you only have one view of the data and thus blah blah blah ... would be my ad infinitum response. Let me point this out: It's there to give you guarantees and ways to think about your code and to allow you to use the same code for mutable and immutable data. That's it. There's no magic behind it that you're not getting. The idea is that if you want those particular features in your code and you know you can use them effectively, then you use const. Generally, if you want the same code to work on mutable data and immutable data, then you have to use const. It's a bridge that allows the same code to work with either type. You can use this so that you reduce code duplication (and work for you) or/and reduce the amount of instructions loaded in your CPU (potentially reducing the times when your cache is reloaded) or I'm sure there's other good reasons as well, but that's for you to figure out. It isn't a catch all feature. Not everything needs to be bashed with the const golden hammer of doom without any thought behind why you might want it to be const. There's plenty of times where you don't want to use const because it simply doesn't make sense and it doesn't give any additional meaning to your code. But that's okay, you just don't need to use const when it doesn't do anything for you. Your main problem seems to be that you're intentionally trying to break const as hard as you can. Obviously, there's nonsensical ways to use const, and I don't think anyone would argue that _any_ feature is completely bulletproof to incompetence. Take classes for instance. Do I really need to give you an example that grossly misuses classes? Does that mean that classes have no purpose? Should I always use structs or parallel arrays because classes can be misused? Instead of trying to misuse the feature, you ought to be spending your energy trying to use the feature for your benefit. (I'm going to make this the last post on the matter ... I'll respond to Mr. Davis' concern on the bug in just a minute, but I've made my full effort to explain const to you and I don't see where additional conversation on my part can clarify this any further.)
Aug 16 2012
prev sibling next sibling parent "Chris Cain" <clcain uncg.edu> writes:
On Friday, 17 August 2012 at 02:02:56 UTC, Jonathan M Davis wrote:
 How is it a bug? The variable that you're altering is not part 
 of the object.
 That's part of why having pure with const in so valuable. It 
 prevents stuff
 like what you're doing here.

 - Jonathan M Davis

Notice that I'm making an immutable(S) in that example. It should then transitively mean that the data pointed to will be immutable as well. The reason this is a bug is because the constructor is currently set up to have the thing you're working with be mutable while you're still constructing it (otherwise, you couldn't even initialize any of the fields). However, this allows invalid code. Here's a bit simplified version with some extra output: int gCount; struct S { int* it; this(int i) { writeln("--- constructor begin ---"); gCount = i; it = &gCount; // Should be compile error //when constructing immutable(S) writeln(typeof(it).stringof, " it = ", *it); writeln("^^ Notice, typeof(it) is always mutable!"); writeln("--- constructor end ---"); } ref const(int*) getItem() const { ++gCount; return it; } } import std.stdio; void main() { immutable(S) s = immutable(S)(0); auto it = s.getItem(); writeln(typeof(s.it).stringof, " s.it = ", *s.it); writeln(typeof(it).stringof, " it = ", *it); writeln(typeof(gCount).stringof, " gCount = ", gCount); s.getItem(); writeln(typeof(s.it).stringof, " s.it = ", *s.it); writeln(typeof(it).stringof, " it = ", *it); writeln(typeof(gCount).stringof, " gCount = ", gCount); } I'm almost sure it's been reported before because I've seen talk about this particular issue in the past. If you run this with optimizations off and on, it shows off the type of optimizations that can be made when something is immutable. For instance, with optimizations off you get: ... immutable(int*) s.it = 1 const(int*) it = 1 int gCount = 1 immutable(int*) s.it = 2 const(int*) it = 2 int gCount = 2 and on: ... immutable(int*) s.it = 1 const(int*) it = 1 int gCount = 1 immutable(int*) s.it = 1 const(int*) it = 2 int gCount = 2 As you can see, the optimization is that it doesn't even check s.it's memory location, it just puts the previous value in place. But yeah, it's a bug that this code is allowed.
Aug 16 2012
prev sibling next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Friday, August 17, 2012 04:30:42 Mehrdad wrote:
 So unless you're expecting the compiler to have the
 implementation for the entire class available in order for it to
 be able to do any kind of optimization (in which case, it would
 have to do a whole bunch of inference to figure out the aliasing
 issues, which would amount to what a C++ could try do just as
 well), I'm not seeing where the additional guarantees/potential
 optimizations are.

It's probably going to be restricted to primitive types in many cases due to not knowing what member functions are doing. But take this code for example: auto i = new int; *i = 5; const c = i; writeln(c); func(c); //obviously takes const or it wouldn't compile writeln(c); The compiler _knows_ that c is the same before and after the call to func, because it knows that no other references to that data can exist. And since it's a built-in type, it also knows that there's no way that any functions operating on func can wile away any mutable, global references to it. So, if we were doing something more interesting than writeln before and after func - something which could actually be optimized based on the knowledge that c is unchanged - then const alone is enough to guarantee that that optimization is valid. The same cannot be said of C++, which could cast away const on c inside of func and do who-knows-what to it. If you use a class instead of int and take it one step further and simply make its constructor pure, then the compiler _still_ knows that the object is the same before and after the call to func, even if none of the class' other functions are pure, because it knows that no mutable references to the data can exist beyond i. Once you're dealing with code which doesn't include the creation of the object, it's likely much harder to make any gurantees about it not being altered, because the compiler doesn't know whether any other references to the data exist or not, and then purity matters that much more, but if the compiler knows that no other references to the data exists, then const is enough, even if the object is passed to other functions, unlike with C++. - Jonathan M Davis
Aug 16 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
On Friday, 17 August 2012 at 02:33:46 UTC, Chris Cain wrote:
 I've already responded to something that is equivalent to what 
 you just posted. I'm not sure if you're intentionally being 
 obtuse (I'll give you the benefit of the doubt)

Thanks... I promise I'm not >__< I'm just having trouble seeing what guarantees anyone is getting.
 or if your eyes are glossing over when you get to the important 
 parts of my posts, but in either case I don't feel like 
 repeating myself again. For your benefit, I'm repeating myself 
 as clearly as I can:

Ok thanks for trying again. :)
 Const is basically a view of your data.
 If you have something that's const, it's like looking through a 
 filter that only allows you to do certain things. It is _not_ a 
 guarantee that it's the only view of your data that exists in 
 the universe.

Yup, just like in C++.
 Your code, again, shows that you have two views of your memory. 
 x is a mutable view of your data, and you're mutating it.

Yup, so far so good.
 y is your const view of your data. No matter how hard you try 
 (without casts), you can't mutate y.

Yup.
 You _can_ mutate x, which y is viewing. The y view updates 
 accordingly, but you didn't mutate y.

Yup.
 Maybe in a second you'll say "oh, but that's mutating y" and 
 I'll promptly ignore you completely this time because you've 
 yet again missed the point:2

Nope, not what I said. :)
 You mutated x, not y, y is a view on the same data that x is so 
 mutating x would logically have y's view updated since const 
 isn't a guarantee that you only have one view of the data and 
 thus blah blah blah ... would be my ad infinitum response.

Yes, I already understood this, and I'm pretty sure I still do. What you're saying, I understand. What is seems to be missing is the answer to my question: What _guarantees_ can the compiler make about your code, /based/ on the fact that y is a const view, or that the entire struct is const? You keep on telling me that y is a const view, which, obviously, it is. But the question is: why is that useful, compared to C++? e.g. What kind of a guarantee can a D compiler make about code that calls a const method, which a C++ compiler probably wouldn't be able to?
 Let me point this out: It's there to give you guarantees and 
 ways to think about your code and to allow you to use the same 
 code for mutable and immutable data. That's it. There's no 
 magic behind it that you're not getting. The idea is that if 
 you want those particular features in your code and you know 
 you can use them effectively, then you use const. Generally, if 
 you want the same code to work on mutable data and immutable 
 data, then you have to use const. It's a bridge that allows the 
 same code to work with either type. You can use this so that 
 you reduce code duplication (and work for you) or/and reduce 
 the amount of instructions loaded in your CPU (potentially 
 reducing the times when your cache is reloaded) or I'm sure 
 there's other good reasons as well, but that's for you to 
 figure out. It isn't a catch all feature. Not everything needs 
 to be bashed with the const golden hammer of doom without any 
 thought behind why you might want it to be const. There's 
 plenty of times where you don't want to use const because it 
 simply doesn't make sense and it doesn't give any additional 
 meaning to your code. But that's okay, you just don't need to 
 use const when it doesn't do anything for you.


 Your main problem seems to be that you're intentionally trying 
 to break const as hard as you can.

"Hacking around", I guess you could put it. :-D As well as trying to clarify things for other people who might have the same questions as well (which I'm sure they would).
 Obviously, there's nonsensical ways to use const, and I don't 
 think anyone would argue that _any_ feature is completely 
 bulletproof to incompetence. Take classes for instance. Do I 
 really need to give you an example that grossly misuses 
 classes? Does that mean that classes have no purpose? Should I 
 always use structs or parallel arrays because classes can be 
 misused?
 Instead of trying to misuse the feature, you ought to be 
 spending your energy trying to use the feature for your benefit.

It's not a question about misuse, it's a question about guarantees. The compiler simply _isn't_ allowed to do something that works for 99% of code, while breaking that 1% that misuses it (but is legal). i.e. It's a code generation issue, not a misuse issue.
 (I'm going to make this the last post on the matter ... I'll 
 respond to Mr. Davis' concern on the bug in just a minute, but 
 I've made my full effort to explain const to you and I don't 
 see where additional conversation on my part can clarify this 
 any further.)

Well ok, nice speaking with you then. :)
Aug 16 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
On Friday, 17 August 2012 at 02:49:45 UTC, Jonathan M Davis wrote:
 But take this code for example:

 auto i = new int;
 *i = 5;
 const c = i;
 writeln(c);
 func(c); //obviously takes const or it wouldn't compile
 writeln(c);

 The compiler _knows_ that c is the same before and after the 
 call to func, because it knows that no other references to that 
 data can exist.

Is there any reason why your example didn't just say
 const(int*) c = null;
 writeln(c);
 func(c);
 writeln(c);

i.e. What was the point of 'i' there? And why can't a C++ compiler do the same thing? 'c' is a const object, so if C++ code was to modify it, it would be undefined behavior, just like in D. Sorry, I'm a little confused at what you were illustrating here.
Aug 16 2012
prev sibling next sibling parent "Chris Cain" <clcain uncg.edu> writes:
Well, since I'm not describing how const works anymore (although, 
it's still different than C++ due to the mutable keyword in C++, 
but I digress), I'll go ahead and jump in for this one...

On Friday, 17 August 2012 at 02:30:45 UTC, Mehrdad wrote:
 So unless you're expecting the compiler to have the 
 implementation for the entire class available in order for it 
 to be able to do any kind of optimization (in which case, it 
 would have to do a whole bunch of inference to figure out the 
 aliasing issues, which would amount to what a C++ could try do 
 just as well), I'm not seeing where the additional 
 guarantees/potential optimizations are.

I'll tackle the guarantees part and what the compiler could do. Combine const and pure and here you go: int global; struct MyStruct { int* y; //this() { } // Stop doing this, it doesn't compile. :P this(int* z) { y = z; } auto getValue() const pure { //++global; // error: cannot access mutable static data 'global' return this.y; } void impureThing() const { ++global; } } void func(ref const(MyStruct) s) pure { //... can only call pure functions // s.impureThing(); // error } import std.stdio; void main() { auto s = MyStruct(&global); writeln(*s.getValue()); func(s); // func is pure and s will be const ... thus, writeln(*s.getValue()); // guaranteed to be the same as first call } And this is different than C++. If C++ did have a "pure" keyword and it worked the same as D's pure keyword, then you still couldn't make the same inference all the time because C++ allows const things to internally mutate due to the mutable keyword. Yeah, you have to start combining features to get the most out of it, but const + pure allows for more optimizations/reasoning than const or pure alone.
Aug 16 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
On Friday, 17 August 2012 at 03:36:28 UTC, Chris Cain wrote:
 Combine const and pure

Yes, I 100% realize 'pure' and 'immutable' are advantages over C++. The question was about 'const' by itself, though, because otherwise that's not a fair comparison. (The goal is comparing C++ const to D const, not C++ const to D const + immutable + purity.)
Aug 16 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
On Friday, 17 August 2012 at 03:42:23 UTC, Mehrdad wrote:
 On Friday, 17 August 2012 at 03:36:28 UTC, Chris Cain wrote:
 Combine const and pure

Yes, I 100% realize 'pure' and 'immutable' are advantages over C++. The question was about 'const' by itself, though, because otherwise that's not a fair comparison. (The goal is comparing C++ const to D const, not C++ const to D const + immutable + purity.)

To clarify... the motivation for this question in the first place was the fact that I've been consistently told (and have read) that D const provides more guarantees than C++ const, so I was trying to figure out how. If what you're saying is that the extra guarantees only translate into optimizations when used along with immutable/pure, then that's a completely valid answer. :) Is that what you're saying? (i.e. I DO realize that combining them would give you an optimization, but the question is -- must you combine const with something else to gain an advantage over C++?)
Aug 16 2012
prev sibling next sibling parent "Chris Cain" <clcain uncg.edu> writes:
On Friday, 17 August 2012 at 03:42:23 UTC, Mehrdad wrote:
 Yes, I 100% realize 'pure' and 'immutable' are advantages over 
 C++.
 The question was about 'const' by itself, though, because 
 otherwise that's not a fair comparison. (The goal is comparing 
 C++ const to D const, not C++ const to D const + immutable + 
 purity.)

I don't know what to tell you then. If you have a non-const pure function and add const to it, then you can make all kinds of optimizations for free basically. But, as you've found, if you completely ignore purity, you can find some ways around that and the compiler would have to potentially perform some expensive checks to do the same optimizations (unless they're just not valid). You can probably do more optimizations on primitive types easier (but they obviously have their own limitations), but many of those same optimizations could potentially be done on C++'s const primitives as well. But really, the biggest differentiation between C++'s const and D's const comes in to play with non-primitives. C++'s const on classes, for instance, gives you _no_ guarantees (none, at all, unless you dive into the class and take a close look) and therefore _no_ optimizations without major costly calculations and certainly no easy reasoning. OTOH, D's const does give you guarantees. Combine that with pure and you've got huge amounts of reasoning. And yes, I'd say this answers your topic's question on what D's const does for you. Clearly, a pure function without const doesn't provide you with the same reasoning as a pure function with const. writeln(s.pureMutatingFunc()); writeln(s.pureMutatingFunc()); // guaranteed not to change globals ... restricted to only changing s and, transitively, everything s points to const: Prevents changing s and, transitively, everything s points to (through the s view, of course ... doesn't say anything about global data or other views available to you because of global data, as you've found). ergo, we can do this: writeln(s.pureConstFunc()); writeln(s.pureConstFunc()); // definitely the same as above call Now an impure function can modify globals, so it shouldn't surprise you that if s has a pointer to a global, you can modify the global and therefor the view s represents will also have a change.
Aug 16 2012
prev sibling next sibling parent "Chris Cain" <clcain uncg.edu> writes:
On Friday, 17 August 2012 at 03:44:38 UTC, Mehrdad wrote:
 To clarify... the motivation for this question in the first 
 place was the fact that I've been consistently told (and have 
 read) that D const provides more guarantees than C++ const, so 
 I was trying to figure out how.


 If what you're saying is that the extra guarantees only 
 translate into optimizations when used along with 
 immutable/pure, then that's a completely valid answer. :) Is 
 that what you're saying?

 (i.e. I DO realize that combining them would give you an 
 optimization, but the question is -- must you combine const 
 with something else to gain an advantage over C++?)

Well, I guess if you're modifying globals and you're going to have your object pointing to those globals, then no you aren't going to get any optimizations that way. And code like that will be harder to reason about as well. I'd have a hard time saying that the "extra guarantees only translate into optimizations when used along with immutable/pure". I'd say in more realistic code where you don't do absurd things like intentionally put a global into a const variable just to mutate the global willy nilly, you'd get optimizations (although, it'd probably have to check for those absurd cases as well, but non-trivial optimizations are still optimizations and it's possible to do). It is something that would be much more difficult to do with C++ ... so much so, that if C++ didn't have the const keyword, I'd imagine the problem would still be roughly the same difficulty. Not only does C++ have to check globals like D would, but it has to check to make sure none of the mutable-marked member variables change, and if they do, then it would have to check to make sure that the change wouldn't result in changes in other functions (even if they're const functions, btw) along the way and ... etc. But yeah, you do get guarantees with D's const that you don't with C++ const (mutable keyword, again, which makes C++'s const useless in all non-trivial cases). I will say that D's features have a synergy about them and combining them with another one will make them more powerful than the two individually. So const + pure is a pretty good combination. That does make many optimizations (that would normally be difficult) completely trivial to do.
Aug 16 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Friday, August 17, 2012 05:11:49 Mehrdad wrote:
 On Friday, 17 August 2012 at 02:49:45 UTC, Jonathan M Davis wrote:
 But take this code for example:
 
 auto i = new int;
 *i = 5;
 const c = i;
 writeln(c);
 func(c); //obviously takes const or it wouldn't compile
 writeln(c);
 
 The compiler _knows_ that c is the same before and after the
 call to func, because it knows that no other references to that
 data can exist.

Is there any reason why your example didn't just say
 const(int*) c = null;
 writeln(c);
 func(c);
 writeln(c);

i.e. What was the point of 'i' there? And why can't a C++ compiler do the same thing? 'c' is a const object, so if C++ code was to modify it, it would be undefined behavior, just like in D. Sorry, I'm a little confused at what you were illustrating here.

1. Because it wasn't creating as const, C++ could legally mutate the object in func by casting away const (meaning that it can't assume that c is unchanged after the call to func), which is not the case in D. But if it were created as const, then it would undefined behavior in both languages. 2. If you want to assign an actual value to c rather than null, you either need to use a helper function or create a mutable one first, because there's no do something like const int* c = new int(5); and have c point to an int with value 5. So, with my example, the D code can guarantee that func doesn't modify either c or what's pointed to by c, whereas C++ provides no such guarantee. So, any optimizations which could be done based on the fact that func didn't change C's value can be done in D but not C++. - Jonathan M Davis
Aug 16 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
On Friday, 17 August 2012 at 03:57:21 UTC, Chris Cain wrote:
 On Friday, 17 August 2012 at 03:42:23 UTC, Mehrdad wrote:
 Yes, I 100% realize 'pure' and 'immutable' are advantages over 
 C++.
 The question was about 'const' by itself, though, because 
 otherwise that's not a fair comparison. (The goal is comparing 
 C++ const to D const, not C++ const to D const + immutable + 
 purity.)

I don't know what to tell you then. If you have a non-const pure function and add const to it, then you can make all kinds of optimizations for free basically. But, as you've found, if you completely ignore purity, you can find some ways around that and the compiler would have to potentially perform some expensive checks to do the same optimizations (unless they're just not valid). You can probably do more optimizations on primitive types easier (but they obviously have their own limitations), but many of those same optimizations could potentially be done on C++'s const primitives as well. But really, the biggest differentiation between C++'s const and D's const comes in to play with non-primitives. C++'s const on classes, for instance, gives you _no_ guarantees (none, at all, unless you dive into the class and take a close look) and therefore _no_ optimizations without major costly calculations and certainly no easy reasoning. OTOH, D's const does give you guarantees. Combine that with pure and you've got huge amounts of reasoning. And yes, I'd say this answers your topic's question on what D's const does for you. Clearly, a pure function without const doesn't provide you with the same reasoning as a pure function with const. writeln(s.pureMutatingFunc()); writeln(s.pureMutatingFunc()); // guaranteed not to change globals ... restricted to only changing s and, transitively, everything s points to const: Prevents changing s and, transitively, everything s points to (through the s view, of course ... doesn't say anything about global data or other views available to you because of global data, as you've found). ergo, we can do this: writeln(s.pureConstFunc()); writeln(s.pureConstFunc()); // definitely the same as above call Now an impure function can modify globals, so it shouldn't surprise you that if s has a pointer to a global, you can modify the global and therefor the view s represents will also have a change.

Okay so basically, the conclusion I'm drawing is that you have to combine it with pure/immutable in order to get much more out of it than compared with C++; otherwise, it's not really different. Thanks!
Aug 16 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Friday, August 17, 2012 04:46:34 Chris Cain wrote:
 Notice that I'm making an immutable(S) in that example.

I missed that. It's a known bug and in bugzilla somewhere. I'd have to go digging to find the exact bug# though. const constructors have the same problem. - Jonathan M Davis
Aug 16 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
On Friday, 17 August 2012 at 04:17:05 UTC, Jonathan M Davis wrote:
 On Friday, August 17, 2012 05:11:49 Mehrdad wrote:
 On Friday, 17 August 2012 at 02:49:45 UTC, Jonathan M Davis 
 wrote:
 But take this code for example:
 
 auto i = new int;
 *i = 5;
 const c = i;
 writeln(c);
 func(c); //obviously takes const or it wouldn't compile
 writeln(c);
 
 The compiler _knows_ that c is the same before and after the
 call to func, because it knows that no other references to 
 that
 data can exist.

Is there any reason why your example didn't just say
 const(int*) c = null;
 writeln(c);
 func(c);
 writeln(c);

i.e. What was the point of 'i' there? And why can't a C++ compiler do the same thing? 'c' is a const object, so if C++ code was to modify it, it would be undefined behavior, just like in D. Sorry, I'm a little confused at what you were illustrating here.

1. Because it wasn't creating as const, C++ could legally mutate the object in func by casting away const (meaning that it can't assume that c is unchanged after the call to func), which is not the case in D. But if it were created as const, then it would undefined behavior in both languages. 2. If you want to assign an actual value to c rather than null, you either need to use a helper function or create a mutable one first, because there's no do something like const int* c = new int(5); and have c point to an int with value 5. So, with my example, the D code can guarantee that func doesn't modify either c or what's pointed to by c, whereas C++ provides no such guarantee. So, any optimizations which could be done based on the fact that func didn't change C's value can be done in D but not C++. - Jonathan M Davis

Oh, so you're talking about the value of 'i' /after/ all that code has executed... I kinda got lost in there because you didn't seem to use 'i' afterwards. Interesting, that's a great example I think. It might be worth putting on the website somewhere, especially because it clearly shows that the compiler doesn't need the source of func to infer that 'i' won't be changed. Cool, thanks a bunch, as always! :)
Aug 16 2012
prev sibling next sibling parent "Chris Cain" <clcain uncg.edu> writes:
On Friday, 17 August 2012 at 04:17:33 UTC, Mehrdad wrote:
 Okay so basically, the conclusion I'm drawing is that you have 
 to combine it with pure/immutable in order to get much more out 
 of it than compared with C++; otherwise, it's not really 
 different.

 Thanks!

You posted this before seeing the additional clarifications Mr. Davis and I posted, and I understand that, but I just wanted to say that factoring what we both said I think it's clear that it is really different. Not bulletproof (I'd say const + pure would be nearly bulletproof and immutable + pure would truly be bulletproof if you started considering multi-threading), but certainly better than C++.
Aug 16 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
On Friday, 17 August 2012 at 04:25:42 UTC, Chris Cain wrote:
 On Friday, 17 August 2012 at 04:17:33 UTC, Mehrdad wrote:
 Okay so basically, the conclusion I'm drawing is that you have 
 to combine it with pure/immutable in order to get much more 
 out of it than compared with C++; otherwise, it's not really 
 different.

 Thanks!

You posted this before seeing the additional clarifications Mr. Davis and I posted, and I understand that, but I just wanted to say that factoring what we both said I think it's clear that it is really different. Not bulletproof (I'd say const + pure would be nearly bulletproof and immutable + pure would truly be bulletproof if you started considering multi-threading), but certainly better than C++.

Yup, I saw the difference after his post as well. :)
Aug 16 2012
prev sibling next sibling parent "Jesse Phillips" <Jessekphillips+D gmail.com> writes:
 On Friday, 17 August 2012 at 02:14:22 UTC, Jonathan M Davis 
 wrote:
 What I meant is that you know that nothing was altered through 
 the reference that the getter returned. You don't have any 
 such guarantee in C++.


Please reread what Jonathan has written above and look at my example below: struct MyStruct { static int* x; int* y; int z; this(int* z) { x = z; } auto getValue() const { ++*x; return this.y; } } void main() { auto s = MyStruct(); s.y = &s.z; auto r = MyStruct(s.y); r.y = r.x; r.getValue(); // const, but returns 1 r.getValue(); // const, but returns 2 r.getValue(); // const, but returns 3 auto m = r.getValue(); *m = 5; // test.d(21): Error: *m is not mutable }
Aug 17 2012
prev sibling next sibling parent "Jesse Phillips" <Jessekphillips+D gmail.com> writes:
On Friday, 17 August 2012 at 01:51:38 UTC, Mehrdad wrote:
 If you did, then the code would be invalid, and the compiler 
 could simply format your C: drive instead of modifying the 
 object.

This is probably the worst discussion point when people talk of why undefined behavior is bad. It is true in that you won't know what happens when in an undefined state, but it is false in that, if it formats your C drive then ~you'd have to be running Windows~, that would be defined behavior and the spec would have said "Implementation Defined" Which actually brings another point "Implementation Defined" is then just as bad as Undefined because the compiler can do whatever it wants! (And yes I know it is bad too). The reason it is left undefined is because either it would have to be prevented by the compiler, or some other change to the specification would have to be made in order to define the behavior (which would be an addition not desired). In the case of D, we define casting, thus you can cast away const. But after that the compiler holds no record it was const, nor does it make any attempt to move the data into read only memory, and thus, if the variable is located in writable memory it can be changed. The question you are asking I do not have an answer for. I will try repeating it here in case it clarifies for them. Without the presence of immutable/pure, what can the compiler do with code that is marked as const that improves performance which is not done in C++. (Actually I'd like to know those which C++ preforms too).
Aug 17 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
On Friday, 17 August 2012 at 21:25:31 UTC, Jesse Phillips wrote:
 On Friday, 17 August 2012 at 01:51:38 UTC, Mehrdad wrote:
 If you did, then the code would be invalid, and the compiler 
 could simply format your C: drive instead of modifying the 
 object.

This is probably the worst discussion point when people talk of why undefined behavior is bad.

I recommend reading these (all three, not just the first one), if you haven't already: http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html http://blog.llvm.org/2011/05/what-every-c-programmer-should-know_14.html http://blog.llvm.org/2011/05/what-every-c-programmer-should-know_21.html
 It is true in that you won't know what happens when in an

drive then ~you'd have to be running Windows~, that would be defined behavior and the spec would have said "Implementation Defined" No, you're completely missing the point. "Implementation defined" and "undefined" are different terms, defined in the C++ standard. Go look them up. (I used to think like you as well, until I was corrected.)
 Which actually brings another point "Implementation Defined" is 
 then just as bad as Undefined because the compiler can do 
 whatever it wants! (And yes I know it is bad too).

I used to think like you too... Implementation defined means "we don't tell you what it means, but the implementation will define it, and you can rely on getting sensible behavior out of it". Undefined means "we don't tell you what it means, and we don't really expect the implementation to tell you, either, so unless the implementation tells you otherwise, if you run into it, your program will be meaningless".
Aug 17 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
On Friday, 17 August 2012 at 20:46:02 UTC, Walter Bright wrote:
 On 8/16/2012 6:43 PM, Mehrdad wrote:
 On Friday, 17 August 2012 at 01:25:18 UTC, Chris Cain wrote:
 Yeah. Again, you can't modify __the const view__.

Isn't that kinda useless, if it tells you nothing about the object itself?

It means you can write code that can process both mutable and immutable objects.

I meant from a C++/D comparison standpoint...
Aug 17 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
On Friday, 17 August 2012 at 22:05:56 UTC, Walter Bright wrote:
 I don't know what you're driving at.

Sorry... I tried to put it in the title. :\
Aug 17 2012
prev sibling next sibling parent "Jesse Phillips" <jessekphillips+D gmail.com> writes:
On Friday, 17 August 2012 at 21:33:28 UTC, Mehrdad wrote:
 On Friday, 17 August 2012 at 21:25:31 UTC, Jesse Phillips wrote:
 On Friday, 17 August 2012 at 01:51:38 UTC, Mehrdad wrote:
 If you did, then the code would be invalid, and the compiler 
 could simply format your C: drive instead of modifying the 
 object.

This is probably the worst discussion point when people talk of why undefined behavior is bad.

I recommend reading these (all three, not just the first one), if you haven't already: http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html http://blog.llvm.org/2011/05/what-every-c-programmer-should-know_14.html http://blog.llvm.org/2011/05/what-every-c-programmer-should-know_21.html

He did not make his case on what of undefined behavior allows him to format your hard drive. Instead it just gives some good examples for what I am talking about. void contains_null_check(int *P) { int dead = *P; // Compiler sees dereference if (P == 0) // Dereference indicates that you can't reach here if null return; *P = 4; } At no point does the compiler know you have enacted undefined behavior. Why? because does not have the information and is not permitted to insert extra information that when P is null do...
 It is true in that you won't know what happens when in an

drive then ~you'd have to be running Windows~, that would be defined behavior and the spec would have said "Implementation Defined" No, you're completely missing the point. "Implementation defined" and "undefined" are different terms, defined in the C++ standard. Go look them up. (I used to think like you as well, until I was corrected.)

I am not missing the point, though it seems there is also "Unspecified" http://stackoverflow.com/a/4105123/34435 I am in agreement that the behavior of executing the code could be a formating of the hard drive. However I do not agree that it is the compiler which can cause this to happen an still conform to the specification. There are other aspects to the specification that would restrict the compilers ability to insert arbitrary code. Undefined behavior is usually identifiable at runtime, the compiler only has compile time information.
Aug 17 2012
prev sibling next sibling parent reply "Jesse Phillips" <jessekphillips+D gmail.com> writes:
On Friday, 17 August 2012 at 22:05:56 UTC, Walter Bright wrote:
 On 8/17/2012 2:34 PM, Mehrdad wrote:
 On Friday, 17 August 2012 at 20:46:02 UTC, Walter Bright wrote:
 On 8/16/2012 6:43 PM, Mehrdad wrote:
 On Friday, 17 August 2012 at 01:25:18 UTC, Chris Cain wrote:
 Yeah. Again, you can't modify __the const view__.

Isn't that kinda useless, if it tells you nothing about the object itself?

It means you can write code that can process both mutable and immutable objects.

I meant from a C++/D comparison standpoint...

I don't know what you're driving at.

He wants to know what optimizations you get from transitive const over C++ const when you ignore other D features such as immutable and pure.
Aug 17 2012
parent Walter Bright <newshound2 digitalmars.com> writes:
On 8/18/2012 9:59 AM, Jonathan M Davis wrote:
 That's not quite true. There _are_ cases where const by itself is enough to
 provide guarantees (and I give such an example elsewhere in this thread).
 However, the situations where that's the case are quite limited, because the
 compiler has to be able to know that no other references to that data exist.
 With pure added in, they increase considerably, and of course, with immutable,
 they're much, much higher, because it doesn't have to worry about figuring out
 whether other, mutable references to the data exist or not. But if the
 compiler knows that no other references to const data exist, then that's
 enough to make optimizations (though whether the compiler ever _does_ do that
 is another matter entirely).

You're quite right.
Aug 18 2012
prev sibling next sibling parent "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Saturday, 18 August 2012 at 04:18:33 UTC, Jesse Phillips wrote:
 On Friday, 17 August 2012 at 22:05:56 UTC, Walter Bright wrote:
 On 8/17/2012 2:34 PM, Mehrdad wrote:
 On Friday, 17 August 2012 at 20:46:02 UTC, Walter Bright 
 wrote:
 On 8/16/2012 6:43 PM, Mehrdad wrote:
 On Friday, 17 August 2012 at 01:25:18 UTC, Chris Cain wrote:
 Yeah. Again, you can't modify __the const view__.

Isn't that kinda useless, if it tells you nothing about the object itself?

It means you can write code that can process both mutable and immutable objects.

I meant from a C++/D comparison standpoint...

I don't know what you're driving at.

He wants to know what optimizations you get from transitive const over C++ const when you ignore other D features such as immutable and pure.

In D, const without immutable is meaningless. const on its own provides no guarantees, it just imposes restrictions so that immutable can provide guarantees. Think of it this way: const without immutable is like an interface with only one implementation. It's pointless: using the interface would just deny you access to other details of the (only) implementation. However, the restrictions provided by the interface (i.e. that the implementations must have certain methods) allows other implementations, and then using the interface is a useful way of writing polymorphic code. const is the interface to mutable and immutable.
Aug 18 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, August 18, 2012 13:25:56 Peter Alexander wrote:
 In D, const without immutable is meaningless.

That's not quite true. There _are_ cases where const by itself is enough to provide guarantees (and I give such an example elsewhere in this thread). However, the situations where that's the case are quite limited, because the compiler has to be able to know that no other references to that data exist. With pure added in, they increase considerably, and of course, with immutable, they're much, much higher, because it doesn't have to worry about figuring out whether other, mutable references to the data exist or not. But if the compiler knows that no other references to const data exist, then that's enough to make optimizations (though whether the compiler ever _does_ do that is another matter entirely). - Jonathan M Davis
Aug 18 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
On Saturday, 18 August 2012 at 11:26:01 UTC, Peter Alexander 
wrote:
 In D, const without immutable is meaningless.

lol, that was the whole point of this question... the whole point of Jon's example was to show it's not meaningless even without immutable. :)
Aug 18 2012
prev sibling next sibling parent "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Saturday, 18 August 2012 at 20:22:59 UTC, Mehrdad wrote:
 On Saturday, 18 August 2012 at 11:26:01 UTC, Peter Alexander 
 wrote:
 In D, const without immutable is meaningless.

lol, that was the whole point of this question... the whole point of Jon's example was to show it's not meaningless even without immutable. :)

Jon is right. If you can guarantee that there are no other references then const can provide some guarantees, but in general you don't know if there are other references.
Aug 18 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
On Saturday, 18 August 2012 at 22:04:21 UTC, Peter Alexander 
wrote:
 On Saturday, 18 August 2012 at 20:22:59 UTC, Mehrdad wrote:
 On Saturday, 18 August 2012 at 11:26:01 UTC, Peter Alexander 
 wrote:
 In D, const without immutable is meaningless.

lol, that was the whole point of this question... the whole point of Jon's example was to show it's not meaningless even without immutable. :)

Jon is right. If you can guarantee that there are no other references then const can provide some guarantees, but in general you don't know if there are other references.

Right, most optimizations are not applicable to general cases.
Aug 18 2012
prev sibling next sibling parent "Jesse Phillips" <jessekphillips+D gmail.com> writes:
On Saturday, 18 August 2012 at 11:26:01 UTC, Peter Alexander 
wrote:

 const on its own provides no guarantees, it just imposes 
 restrictions so that immutable can provide guarantees.

While in context with the original question this is fine, but I do not like this use of guarantee. What I mean is, const does provide guarantees by itself. And it provides more than C++ because it is transitive and modifying a const reference is undefined.
Aug 19 2012
prev sibling next sibling parent "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Sunday, 19 August 2012 at 19:26:58 UTC, Jesse Phillips wrote:
 On Saturday, 18 August 2012 at 11:26:01 UTC, Peter Alexander 
 wrote:

 const on its own provides no guarantees, it just imposes 
 restrictions so that immutable can provide guarantees.

While in context with the original question this is fine, but I do not like this use of guarantee. What I mean is, const does provide guarantees by itself. And it provides more than C++ because it is transitive and modifying a const reference is undefined.

What guarantees does const provide on its own?
Aug 19 2012
prev sibling next sibling parent "Era Scarecrow" <rtcvb32 yahoo.com> writes:
On Sunday, 19 August 2012 at 19:42:20 UTC, Peter Alexander wrote:
 On Sunday, 19 August 2012 at 19:26:58 UTC, Jesse Phillips wrote:
 While in context with the original question this is fine, but 
 I do not like this use of guarantee.

 What I mean is, const does provide guarantees by itself. And 
 it provides more than C++ because it is transitive and 
 modifying a const reference is undefined.

What guarantees does const provide on its own?

If you don't circumvent the language by casting/forcing it, then const (and immutable) items cannot be changed by the functions called with them, nor them nor anything they contain or reference. In cases of methods to classes/structs, const ensures the object (allocated separately in each variable/heap location) won't change in any way. Isn't that good enough? It's like asking what guarantees a book contains it's data. It's entirely guaranteed as long you don't use a permenant marker, rip the pages out, burn it, soak it in a bath tub full of water or acid, run it over, throw it in lava, bleach it, or send it through a trans-dimensional portal to another universe. or any number of other things, but that's outside the scope of the publisher's ability to after providing the book; when left untouched the book should always contain the data. Isn't that good enough?
Aug 19 2012
prev sibling next sibling parent "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Sunday, 19 August 2012 at 19:58:11 UTC, Era Scarecrow wrote:
 On Sunday, 19 August 2012 at 19:42:20 UTC, Peter Alexander 
 wrote:
 On Sunday, 19 August 2012 at 19:26:58 UTC, Jesse Phillips 
 wrote:
 While in context with the original question this is fine, but 
 I do not like this use of guarantee.

 What I mean is, const does provide guarantees by itself. And 
 it provides more than C++ because it is transitive and 
 modifying a const reference is undefined.

What guarantees does const provide on its own?

If you don't circumvent the language by casting/forcing it, then const (and immutable) items cannot be changed by the functions called with them, nor them nor anything they contain or reference.

class Foo { static Foo sneaky; this() { sneaky = this; } void bar() const { sneaky.x++; } int x = 0; } const(Foo) f = new Foo(); assert(f.x == 0); f.bar(); assert(f.x == 1); You only have that guarantee if there are no other mutable references to the data. const *on its own* does not provide that guarantee.
Aug 19 2012
prev sibling next sibling parent "Era Scarecrow" <rtcvb32 yahoo.com> writes:
On Sunday, 19 August 2012 at 20:14:50 UTC, Peter Alexander wrote:
 class Foo
 {
     static Foo sneaky;
     this() { sneaky = this; }
     void bar() const { sneaky.x++; }
     int x = 0;
 }

 const(Foo) f = new Foo();
 assert(f.x == 0);
 f.bar();
 assert(f.x == 1);

 You only have that guarantee if there are no other mutable 
 references to the data. const *on its own* does not provide 
 that guarantee.

I don't think 'sneaky' counts. Static being there's only one of basically takes it outside the scope of the class/struct in it's own memory space. Re-written it's treated like this. Foo sneaky; class Foo { this() { sneaky = this; } void bar() const { sneaky.x++; } int x = 0; } const(Foo) f = new Foo(); assert(f.x == 0); f.bar(); assert(f.x == 1); const(Foo) f2 = new Foo(); //a WTF moment? assert(f.x == 1); f.bar(); f.bar(); assert(f.x == 1); assert(f2.x == 2); Besides likely the const/immutable and constructors can likely fix this, or maybe not. *shrugs* More likely the failures. In truth bar never modifies the allocated data (being Foo.x), sneaky although a reference is considered it's own thing. Besides weren't not talking about the data never changing, only the code calling can't change it (directly). If you don't want things to change, then A) don't write it to circumvent it, or B) Use immutable (then do Step A again)
Aug 19 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, August 19, 2012 22:14:49 Peter Alexander wrote:
 You only have that guarantee if there are no other mutable
 references to the data. const *on its own* does not provide that
 guarantee.

Yeah. If you really want to get down to the nitty gritty of when exactly the compiler can guarantee that a const object won't change even through other references, then you're restricted to cases where the compiler _knows_ that no other mutable references exist, which is very restrictive. But in practice, it buys you a lot more, and not only because of pure. For instance, if I have a class that holds a container, and the only place that that data leaves my class is its getter property const(Array!T) myContainer() const {...} then I know that no one using the getter is going to mess with the container. I know that there's no way that they can possibly screw with my container or its contents via the getter. And since of course, I won't have written screwy stuff into my class like having the container really point to a global or even be set via a setter, I know that they won't even be able to mess with the container via another reference. And if I control where the elements come from, then I can know that the elements won't be messed with by any other references either, because I'll know that there are no other references to them. Sure, the compiler may not be able to optimize much (if anything) there, but it really helps you reason about your code - especially when it comes to giving access to member variables to code outside your class or struct. So, the combination of transivity and illegality of mutating const (even through casting) does actually buy you quite a lot. Yes, adding in pure or immutable buys you a lot more, but that doesn't mean that const is useless without them. - Jonathan M Davis
Aug 19 2012
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 16 Aug 2012 18:14:27 -0400, Mehrdad <wfunction hotmail.com> wrote:

 Something I'm having trouble undertanding/remembering (sorry, you've  
 probaby already explained it a billion times)...

 I remember being told many times that D's 'const' provides stronger  
 guarantees than C++'s 'const'.

 I just wanted to clarify, is that true for 'const' itself, or is that  
 referring only to when 'const' is used with other D-specific features  
 (immutable, etc.)?

I know this is old, just going through my backlog of unread newsgroup posts. someone else correctly pointed out that const guarantees make it a viable interface for both immutable and mutable data. In other words, it provides the ability to write a function that accepts both mutable and immutable data, rather than having to write two such functions that would be identical. I look at it like this: immutable provides guarantees such that statically-checked pure is possible. const provides guarantees such that I don't have to repeat myself for everything! inout provides similar benefits as const, but allows me to to use chaining without repeating myself for everything.
 If it's the former, is there some example piece of code in each  
 language, for comparison, that shows how the compiler can infer more  
 from D's const than C++'s?

It's the latter, not the former. The compiler cannot make any assumptions unless the other guarantees (particularly immutable) are involved. For example, consider the following function: int foo(const int *x, int *y) { *y += *x; return *x; } If compiled without any context, the compiler *must* re-load *x from memory, because potentially x == y. However, if called like this: int bar(immutable int *x, int *y) { return foo(x, y); } now, if foo is inlined inside of bar, it *can* make the assumption that x != y, and so a reload of *x is not necessary (it must be unchanged). -Steve
Sep 07 2012