digitalmars.D - Why struct opEquals must be const?
- Nick Sabalausky (13/13) Oct 16 2010 Is there a technical reason why the l- and r- values for opEquals must b...
- Denis Koroskin (3/23) Oct 17 2010 I don't think there any. None of the Object's methods are const (e.g.
- Jonathan M Davis (13/39) Oct 17 2010 They're supposed to be const. There's a long-standing bug over the fact ...
- so (7/27) Oct 18 2010 If you want to invoke a generic function that calls some methods of your...
- Steven Schveighoffer (3/23) Oct 18 2010 http://d.puremagic.com/issues/show_bug.cgi?id=3659
- Pelle (3/28) Oct 18 2010 Shouldn't the compiler make the opEquals non-const if one of the struct
- Steven Schveighoffer (11/44) Oct 18 2010 IMO, opEquals should allow non-const, but only if the type is a value ty...
- Pelle (6/20) Oct 18 2010 I was thinking along the lines of not enforcing unexpected restrictions
- Steven Schveighoffer (8/18) Oct 18 2010 But D also has a good goal of not enabling bad design. Even if through ...
- Pelle (3/22) Oct 18 2010 I agree with this philosophy :-) I do not think it should be compiler
- Rainer Deyke (21/23) Oct 18 2010 What about caching?
- Steven Schveighoffer (10/31) Oct 18 2010 The object's state is still changing. If you don't consider hashValue t...
- Rainer Deyke (9/20) Oct 18 2010 Yes, that's my point. D doesn't have "logical const" like C++, so not
- Steven Schveighoffer (8/26) Oct 19 2010 Then you cannot call opEquals with a const or immutable object. To say ...
- bearophile (5/6) Oct 18 2010 See also, partially related:
Is there a technical reason why the l- and r- values for opEquals must be const? If the restriction is purely for the intuitive notion that there's no heisenstructs, then I have an example I think might be worth consideration: lazy caching. If comparison isn't always needed and requires a potentially expensive computation that isn't likely needed otherwise (for example, a wrapper for string that does case-insensitive comparisons, if it's used in a situation that does more assigning/slicing/etc than comparing), then it may make sense to wait until an opEquals is called, and then compute the information and cache it. But that requires mutating state and so can't be done in a struct opEquals. 'Course, if there is a technical reason for the restriction, then all this is moot.
Oct 16 2010
On Sun, 17 Oct 2010 10:57:02 +0400, Nick Sabalausky <a a.a> wrote:Is there a technical reason why the l- and r- values for opEquals must be const? If the restriction is purely for the intuitive notion that there's no heisenstructs, then I have an example I think might be worth consideration: lazy caching. If comparison isn't always needed and requires a potentially expensive computation that isn't likely needed otherwise (for example, a wrapper for string that does case-insensitive comparisons, if it's used in a situation that does more assigning/slicing/etc than comparing), then it may make sense to wait until an opEquals is called, and then compute the information and cache it. But that requires mutating state and so can't be done in a struct opEquals. 'Course, if there is a technical reason for the restriction, then all this is moot.I don't think there any. None of the Object's methods are const (e.g. opEquals, toHash etc), why would struct need to be const?
Oct 17 2010
On Sunday 17 October 2010 00:02:00 Denis Koroskin wrote:On Sun, 17 Oct 2010 10:57:02 +0400, Nick Sabalausky <a a.a> wrote:They're supposed to be const. There's a long-standing bug over the fact that they aren't const: http://d.puremagic.com/issues/show_bug.cgi?id=1824 The fact that they aren't const is a big problem. opEquals(), toHash(), etc. need to work on const and immutable objects. If they don't, const and immutable quickly start becoming useless. And really, for const and immutable to work well, we really should be trying to make _more_ functions const, not fewer. Now, I don't see any reason why we couldn't have a const and non-const version of functions like opEquals() and toHash() and have non-const objects use the non-const versions and take advantage of whatever caching facilities they might provide, but we _need_ to have const versions of these functions for const and immutable to be properly useable. - Jonathan M DavisIs there a technical reason why the l- and r- values for opEquals must be const? If the restriction is purely for the intuitive notion that there's no heisenstructs, then I have an example I think might be worth consideration: lazy caching. If comparison isn't always needed and requires a potentially expensive computation that isn't likely needed otherwise (for example, a wrapper for string that does case-insensitive comparisons, if it's used in a situation that does more assigning/slicing/etc than comparing), then it may make sense to wait until an opEquals is called, and then compute the information and cache it. But that requires mutating state and so can't be done in a struct opEquals. 'Course, if there is a technical reason for the restriction, then all this is moot.I don't think there any. None of the Object's methods are const (e.g. opEquals, toHash etc), why would struct need to be const?
Oct 17 2010
If you want to invoke a generic function that calls some methods of your input, most of the time you need to be sure these methods are "const-correct", especially when your input is const. It is both a good and bad thing. On Sun, 17 Oct 2010 09:57:02 +0300, Nick Sabalausky <a a.a> wrote:Is there a technical reason why the l- and r- values for opEquals must be const? If the restriction is purely for the intuitive notion that there's no heisenstructs, then I have an example I think might be worth consideration: lazy caching. If comparison isn't always needed and requires a potentially expensive computation that isn't likely needed otherwise (for example, a wrapper for string that does case-insensitive comparisons, if it's used in a situation that does more assigning/slicing/etc than comparing), then it may make sense to wait until an opEquals is called, and then compute the information and cache it. But that requires mutating state and so can't be done in a struct opEquals. 'Course, if there is a technical reason for the restriction, then all this is moot.-- Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
Oct 18 2010
On Sun, 17 Oct 2010 02:57:02 -0400, Nick Sabalausky <a a.a> wrote:Is there a technical reason why the l- and r- values for opEquals must be const? If the restriction is purely for the intuitive notion that there's no heisenstructs, then I have an example I think might be worth consideration: lazy caching. If comparison isn't always needed and requires a potentially expensive computation that isn't likely needed otherwise (for example, a wrapper for string that does case-insensitive comparisons, if it's used in a situation that does more assigning/slicing/etc than comparing), then it may make sense to wait until an opEquals is called, and then compute the information and cache it. But that requires mutating state and so can't be done in a struct opEquals. 'Course, if there is a technical reason for the restriction, then all this is moot.http://d.puremagic.com/issues/show_bug.cgi?id=3659 -Steve
Oct 18 2010
On 10/18/2010 02:41 PM, Steven Schveighoffer wrote:On Sun, 17 Oct 2010 02:57:02 -0400, Nick Sabalausky <a a.a> wrote:Shouldn't the compiler make the opEquals non-const if one of the struct members requires mutability for equality testing? Just a thought.Is there a technical reason why the l- and r- values for opEquals must be const? If the restriction is purely for the intuitive notion that there's no heisenstructs, then I have an example I think might be worth consideration: lazy caching. If comparison isn't always needed and requires a potentially expensive computation that isn't likely needed otherwise (for example, a wrapper for string that does case-insensitive comparisons, if it's used in a situation that does more assigning/slicing/etc than comparing), then it may make sense to wait until an opEquals is called, and then compute the information and cache it. But that requires mutating state and so can't be done in a struct opEquals. 'Course, if there is a technical reason for the restriction, then all this is moot.http://d.puremagic.com/issues/show_bug.cgi?id=3659 -Steve
Oct 18 2010
On Mon, 18 Oct 2010 10:49:25 -0400, Pelle <pelle.mansson gmail.com> wrote:On 10/18/2010 02:41 PM, Steven Schveighoffer wrote:IMO, opEquals should allow non-const, but only if the type is a value type (no references). This means opEquals on a class should always be const. Allowing opEquals to modify the original object violates the expectation of opEquals -- you don't expect comparison to change the objects being compared. But logically, opEquals shouldn't require any specific signature -- it's just another function. What case were you thinking of for requiring mutablity for equality testing? -SteveOn Sun, 17 Oct 2010 02:57:02 -0400, Nick Sabalausky <a a.a> wrote:Shouldn't the compiler make the opEquals non-const if one of the struct members requires mutability for equality testing? Just a thought.Is there a technical reason why the l- and r- values for opEquals must be const? If the restriction is purely for the intuitive notion that there's no heisenstructs, then I have an example I think might be worth consideration: lazy caching. If comparison isn't always needed and requires a potentially expensive computation that isn't likely needed otherwise (for example, a wrapper for string that does case-insensitive comparisons, if it's used in a situation that does more assigning/slicing/etc than comparing), then it may make sense to wait until an opEquals is called, and then compute the information and cache it. But that requires mutating state and so can't be done in a struct opEquals. 'Course, if there is a technical reason for the restriction, then all this is moot.http://d.puremagic.com/issues/show_bug.cgi?id=3659 -Steve
Oct 18 2010
On 10/18/2010 05:07 PM, Steven Schveighoffer wrote:On Mon, 18 Oct 2010 10:49:25 -0400, Pelle <pelle.mansson gmail.com> wrote:I was thinking along the lines of not enforcing unexpected restrictions on programmers because we couldn't think of a use case. This isn't an enabler. Today, the check isn't very intelligent, which makes immutable structs unusable.Shouldn't the compiler make the opEquals non-const if one of the struct members requires mutability for equality testing? Just a thought.IMO, opEquals should allow non-const, but only if the type is a value type (no references). This means opEquals on a class should always be const. Allowing opEquals to modify the original object violates the expectation of opEquals -- you don't expect comparison to change the objects being compared. But logically, opEquals shouldn't require any specific signature -- it's just another function. What case were you thinking of for requiring mutablity for equality testing? -Steve
Oct 18 2010
On Mon, 18 Oct 2010 11:19:54 -0400, Pelle <pelle.mansson gmail.com> wrote:On 10/18/2010 05:07 PM, Steven Schveighoffer wrote:But D also has a good goal of not enabling bad design. Even if through convention. I personally don't see how opEquals can be forced into a particular signature (the current rules are too strict), but a philosophy that opEquals should refrain from changing the objects in question is a good one to have.What case were you thinking of for requiring mutablity for equality testing? -SteveI was thinking along the lines of not enforcing unexpected restrictions on programmers because we couldn't think of a use case. This isn't an enabler.Today, the check isn't very intelligent, which makes immutable structs unusable.Absolutely, hence the bug report. -Steve
Oct 18 2010
On 10/18/2010 05:55 PM, Steven Schveighoffer wrote:On Mon, 18 Oct 2010 11:19:54 -0400, Pelle <pelle.mansson gmail.com> wrote:I agree with this philosophy :-) I do not think it should be compiler enforced, however.On 10/18/2010 05:07 PM, Steven Schveighoffer wrote:But D also has a good goal of not enabling bad design. Even if through convention. I personally don't see how opEquals can be forced into a particular signature (the current rules are too strict), but a philosophy that opEquals should refrain from changing the objects in question is a good one to have.What case were you thinking of for requiring mutablity for equality testing? -SteveI was thinking along the lines of not enforcing unexpected restrictions on programmers because we couldn't think of a use case. This isn't an enabler.Today, the check isn't very intelligent, which makes immutable structs unusable.Absolutely, hence the bug report. -Steve
Oct 18 2010
On 10/18/2010 09:07, Steven Schveighoffer wrote:What case were you thinking of for requiring mutablity for equality testing?What about caching? class C { private int hashValue = -1; // Mutators reset hashValue to -1. int getHash() { if (this.hashValue == -1) { this.hashValue = longExpensiveCalculation(); } return this.hashValue(); } bool opEquals(C other) { if (this.getHash() == other.getHash()) { return slowElementwiseCompare(this, other); } else { return false; } } } -- Rainer Deyke - rainerd eldwood.com
Oct 18 2010
On Mon, 18 Oct 2010 15:43:29 -0400, Rainer Deyke <rainerd eldwood.com> wrote:On 10/18/2010 09:07, Steven Schveighoffer wrote:The object's state is still changing. If you don't consider hashValue to be part of the object's state, then that's what we call 'logical const' (implemented in C++ via the mutable keyword). Only certain members get such privileged status (such as the monitor object). If you think it's worth doing, you can always cast. Despite the label of 'undefined behavior', I believe the compiler will behave fine if you do that (it can't really optimize out const calls). -SteveWhat case were you thinking of for requiring mutablity for equality testing?What about caching? class C { private int hashValue = -1; // Mutators reset hashValue to -1. int getHash() { if (this.hashValue == -1) { this.hashValue = longExpensiveCalculation(); } return this.hashValue(); } bool opEquals(C other) { if (this.getHash() == other.getHash()) { return slowElementwiseCompare(this, other); } else { return false; } } }
Oct 18 2010
On 10/18/2010 13:51, Steven Schveighoffer wrote:On Mon, 18 Oct 2010 15:43:29 -0400, Rainer Deyke <rainerd eldwood.com> wrote:On 10/18/2010 09:07, Steven Schveighoffer wrote:What case were you thinking of for requiring mutablity for equality testing?What about caching?The object's state is still changing.Yes, that's my point. D doesn't have "logical const" like C++, so not all logically const operations can be declared const. opEquals is always logically const, but it cannot always be physically const.If you think it's worth doing, you can always cast. Despite the label of 'undefined behavior', I believe the compiler will behave fine if you do that (it can't really optimize out const calls).Not safe. The set of const objects includes immutable objects, and immutable objects can be accessed from multiple threads at the same time, but caching is not (by default) thread-safe. -- Rainer Deyke - rainerd eldwood.com
Oct 18 2010
On Mon, 18 Oct 2010 17:31:31 -0400, Rainer Deyke <rainerd eldwood.com> wrote:On 10/18/2010 13:51, Steven Schveighoffer wrote:Then you cannot call opEquals with a const or immutable object. To say you're not allowed to compare const or immutable objects is pretty limited.On Mon, 18 Oct 2010 15:43:29 -0400, Rainer Deyke <rainerd eldwood.com> wrote:On 10/18/2010 09:07, Steven Schveighoffer wrote:What case were you thinking of for requiring mutablity for equality testing?What about caching?The object's state is still changing.Yes, that's my point. D doesn't have "logical const" like C++, so not all logically const operations can be declared const. opEquals is always logically const, but it cannot always be physically const.You have to take steps to make sure it is. IMO, your code actually is thread safe because overwriting an int with the same int will not result in any races. -SteveIf you think it's worth doing, you can always cast. Despite the label of 'undefined behavior', I believe the compiler will behave fine if you do that (it can't really optimize out const calls).Not safe. The set of const objects includes immutable objects, and immutable objects can be accessed from multiple threads at the same time, but caching is not (by default) thread-safe.
Oct 19 2010
Steven Schveighoffer:http://d.puremagic.com/issues/show_bug.cgi?id=3659See also, partially related: http://d.puremagic.com/issues/show_bug.cgi?id=4290 Bye, bearophile
Oct 18 2010