digitalmars.D.learn - const and immutable member variables in classes
- =?UTF-8?Q?Ali_=c3=87ehreli?= (15/15) Feb 02 2016 const and immutable members make structs non-assignable:
- anonymous (7/20) Feb 02 2016 I'm not sure what you mean by "default assignment". I'd say it works
- =?UTF-8?Q?Ali_=c3=87ehreli?= (5/12) Feb 02 2016 Exactly. This aspect of reference types had not occurred to me until
- Jonathan M Davis via Digitalmars-d-learn (14/28) Feb 02 2016 Default initialization isn't really the issue. It's assignability in
- H. S. Teoh via Digitalmars-d-learn (34/48) Feb 02 2016 [...]
- Jonathan M Davis via Digitalmars-d-learn (14/59) Feb 02 2016 Well, in principle, if you have a member that's going to be initialized ...
- =?UTF-8?Q?Ali_=c3=87ehreli?= (11/22) Feb 02 2016 would
- Jonathan M Davis via Digitalmars-d-learn (12/17) Feb 02 2016 IIRC, I've pretty much always been telling folks specifically to not hav...
- H. S. Teoh via Digitalmars-d-learn (14/34) Feb 02 2016 [...]
- Jonathan M Davis (17/27) Feb 03 2016 You make a member variable const or immutable for the same
const and immutable members make structs non-assignable: struct S { const int c; // Makes S non-assignable immutable int i; // Makes S non-assignable } void main() { auto a = S(); auto b = S(); a = b; // Compilation ERROR } (That is the same issue in C++.) That's why I've been avoiding them altogether. However, considering that there is no default-assignment for classes, there is no problem with using const or immutable members with classes, right? Ali
Feb 02 2016
On 02.02.2016 23:48, Ali Çehreli wrote:struct S { const int c; // Makes S non-assignable immutable int i; // Makes S non-assignable } void main() { auto a = S(); auto b = S(); a = b; // Compilation ERROR } (That is the same issue in C++.) That's why I've been avoiding them altogether. However, considering that there is no default-assignment for classes, there is no problem with using const or immutable members with classes, right?I'm not sure what you mean by "default assignment". I'd say it works more simply with classes, because they're reference types. It's the same as using pointers to structs: auto a = new S(); auto b = new S(); a = b; /* no problem */
Feb 02 2016
On 02/02/2016 03:02 PM, anonymous wrote:I'm not sure what you mean by "default assignment".I think I meant member-wise assignment. :)I'd say it works more simply with classes, because they're reference types. It's the same as using pointers to structs: auto a = new S(); auto b = new S(); a = b; /* no problem */Exactly. This aspect of reference types had not occurred to me until recently. Ali
Feb 02 2016
On Tuesday, February 02, 2016 14:48:03 Ali ehreli via Digitalmars-d-learn wrote:const and immutable members make structs non-assignable: struct S { const int c; // Makes S non-assignable immutable int i; // Makes S non-assignable } void main() { auto a = S(); auto b = S(); a = b; // Compilation ERROR } (That is the same issue in C++.) That's why I've been avoiding them altogether. However, considering that there is no default-assignment for classes, there is no problem with using const or immutable members with classes, right?Default initialization isn't really the issue. It's assignability in general. Even if you disabled default initialization in a struct, then having a const or immutable member would be annoying, because you couldn't assign it another value later. But in any case, no, classes don't have that problem (at least not normally), because you don't assign to them. You just assign to their references. So, you don't normally run into an issue where you're trying to overwrite the state of a class object and aren't able to. Now, you _can_ run into issues if you want to provide a way to overwrite the whole state of a class object similar to assigning to a struct, but the class would have to be specially written to support that via some kind of non-standard function in order to support that, and you could just avoid giving such a class any const or immutable members. - Jonathan M Davis
Feb 02 2016
On Tue, Feb 02, 2016 at 05:49:00PM -0800, Jonathan M Davis via Digitalmars-d-learn wrote:On Tuesday, February 02, 2016 14:48:03 Ali Çehreli via Digitalmars-d-learn wrote:[...]const and immutable members make structs non-assignable:[...] In general, I find const/immutable members more of a pain than anything else. Such aggregates are extremely cumbersome to work with, and besides, I've yet to come up with a conceptually-clean mental model of just what kind of semantics exactly an aggregate with a const/immutable member "ought" to have. *Reference* members to const/immutable data make sense -- these include string members, for example, as immutable(char)[]. It's not a problem because the reference itself is mutable, just not what it refers to. It's also not a problem when the entire aggregate is const or immutable, e.g., when passing by const to a const member, for example. By transitivity, it's clear that the entire aggregate is const / immutable, so every member is also transitively so. But when a member itself is const or immutable, things become really icky. A mutable instance of a struct with a const member is in a weird mixed state of being partially mutable and partially not. You can never create an instance of the struct that's fully mutable, so even when given a mutable instance, you can't overwrite it, you can't assign to it, etc.. It's also unclear what guarantee the const/immutable member is supposed to provide. In the case of an argument to a function, it's clear that const means the callee promises not to modify, and immutable means the caller (and callee) promise never to modify. But what promise does a const/immutable member give? That nobody can ever modify, except this only applies to that member and the rest of the aggregate may still be mutable? It's rather unclean semantics that leads to all sorts of needlessly convoluted cases, with hard-to-grasp results, and it's hard to think of a use case that requires such a construct, that can't be done some other way. T -- Любишь кататься - люби и саночки возить.That's why I've been avoiding them altogether. However, considering that there is no default-assignment for classes, there is no problem with using const or immutable members with classes, right?Default initialization isn't really the issue. It's assignability in general. Even if you disabled default initialization in a struct, then having a const or immutable member would be annoying, because you couldn't assign it another value later. But in any case, no, classes don't have that problem (at least not normally), because you don't assign to them. You just assign to their references. So, you don't normally run into an issue where you're trying to overwrite the state of a class object and aren't able to.
Feb 02 2016
On Tuesday, February 02, 2016 18:14:50 H. S. Teoh via Digitalmars-d-learn wrote:On Tue, Feb 02, 2016 at 05:49:00PM -0800, Jonathan M Davis via Digitalmars-d-learn wrote:Well, in principle, if you have a member that's going to be initialized on construction and then never mutated, having it be const or immutable would be desirable, but for all of the reasons that you're listing, to do so with structs is ultimately a bad idea. Things just get weird and annoying when you have const or immutable members in mutable structs. But under normal circumstances, member variables of classes don't have that problem, because you don't normally ever try to overwrite the state of the whole class object. But for some reason, I keep seeing folks post stuff where they've tried to have const or immutable members of structs. I don't know if the folks who do so typically give up on it, but I usually tell folks not to do it because of all of the problems it causes. - Jonathan M DavisOn Tuesday, February 02, 2016 14:48:03 Ali ehreli via Digitalmars-d-learn wrote:[...]const and immutable members make structs non-assignable:[...] In general, I find const/immutable members more of a pain than anything else. Such aggregates are extremely cumbersome to work with, and besides, I've yet to come up with a conceptually-clean mental model of just what kind of semantics exactly an aggregate with a const/immutable member "ought" to have. *Reference* members to const/immutable data make sense -- these include string members, for example, as immutable(char)[]. It's not a problem because the reference itself is mutable, just not what it refers to. It's also not a problem when the entire aggregate is const or immutable, e.g., when passing by const to a const member, for example. By transitivity, it's clear that the entire aggregate is const / immutable, so every member is also transitively so. But when a member itself is const or immutable, things become really icky. A mutable instance of a struct with a const member is in a weird mixed state of being partially mutable and partially not. You can never create an instance of the struct that's fully mutable, so even when given a mutable instance, you can't overwrite it, you can't assign to it, etc.. It's also unclear what guarantee the const/immutable member is supposed to provide. In the case of an argument to a function, it's clear that const means the callee promises not to modify, and immutable means the caller (and callee) promise never to modify. But what promise does a const/immutable member give? That nobody can ever modify, except this only applies to that member and the rest of the aggregate may still be mutable? It's rather unclean semantics that leads to all sorts of needlessly convoluted cases, with hard-to-grasp results, and it's hard to think of a use case that requires such a construct, that can't be done some other way.That's why I've been avoiding them altogether. However, considering that there is no default-assignment for classes, there is no problem with using const or immutable members with classes, right?Default initialization isn't really the issue. It's assignability in general. Even if you disabled default initialization in a struct, then having a const or immutable member would be annoying, because you couldn't assign it another value later. But in any case, no, classes don't have that problem (at least not normally), because you don't assign to them. You just assign to their references. So, you don't normally run into an issue where you're trying to overwrite the state of a class object and aren't able to.
Feb 02 2016
On 02/02/2016 07:05 PM, Jonathan M Davis via Digitalmars-d-learn wrote:Well, in principle, if you have a member that's going to beinitialized onconstruction and then never mutated, having it be const or immutablewouldbe desirable,That's how I've seen it used in a friend's code. Makes sense to me as long as it's not a struct.but for all of the reasons that you're listing, to do so with structs is ultimately a bad idea.Agreed.But under normal circumstances, member variables of classes don't have that problem,becauseyou don't normally ever try to overwrite the state of the whole class object.Exactly my point.I usually tell folks not to do it because of all of the problems it causes.That's been my guideline: "const and immutable members should be avoided." Now I'm tempted to add this: "only in structs." Ali
Feb 02 2016
On Tuesday, February 02, 2016 19:32:39 Ali ehreli via Digitalmars-d-learn wrote:On 02/02/2016 07:05 PM, Jonathan M Davis via Digitalmars-d-learn wrote: > I usually tell folks not to do it because of > all of the problems it causes. That's been my guideline: "const and immutable members should be avoided." Now I'm tempted to add this: "only in structs."IIRC, I've pretty much always been telling folks specifically to not have structs with const or immutable members and haven't generally said anything about classes. But I really don't see any reason to avoid it in classes at this point. Maybe if a serialization mechanism were involved, it would matter, but the class would probably have to be designed to support that given that there is no equivalent to the assignment operator for the class objects themselves (just their references). Ultimately, it's the ability to overwrite the state of the object that's the problem, and that simply isn't an issue with class objects under normal circumstances. - Jonathan M Davis
Feb 02 2016
On Tue, Feb 02, 2016 at 09:08:07PM -0800, Jonathan M Davis via Digitalmars-d-learn wrote:On Tuesday, February 02, 2016 19:32:39 Ali ehreli via Digitalmars-d-learn wrote:[...] I still can't come up with a compelling use case that would justify using a const/immutable class member, that couldn't be done by some other means, though. Especially since we're talking about classes, we already have all the traditional OO mechanisms for controlling access to members - get methods, and so on, which are more flexible and adaptable to different use cases to begin with (e.g., can be overridden by derived classes, can implement custom access criteria not expressible by const/immutable, etc.), so I have a hard time justifying using const/immutable members instead. T -- The computer is only a tool. Unfortunately, so is the user. -- Armaphine, K5On 02/02/2016 07:05 PM, Jonathan M Davis via Digitalmars-d-learn wrote: > I usually tell folks not to do it because of all of the problems > it causes. That's been my guideline: "const and immutable members should be avoided." Now I'm tempted to add this: "only in structs."IIRC, I've pretty much always been telling folks specifically to not have structs with const or immutable members and haven't generally said anything about classes. But I really don't see any reason to avoid it in classes at this point. Maybe if a serialization mechanism were involved, it would matter, but the class would probably have to be designed to support that given that there is no equivalent to the assignment operator for the class objects themselves (just their references). Ultimately, it's the ability to overwrite the state of the object that's the problem, and that simply isn't an issue with class objects under normal circumstances.
Feb 02 2016
On Wednesday, 3 February 2016 at 06:11:07 UTC, H. S. Teoh wrote:I still can't come up with a compelling use case that would justify using a const/immutable class member, that couldn't be done by some other means, though. Especially since we're talking about classes, we already have all the traditional OO mechanisms for controlling access to members - get methods, and so on, which are more flexible and adaptable to different use cases to begin with (e.g., can be overridden by derived classes, can implement custom access criteria not expressible by const/immutable, etc.), so I have a hard time justifying using const/immutable members instead.You make a member variable const or immutable for the same reasons that you make a local variable const or immutable - it prevents accidental mutation and potentially makes it so that the compiler can optimize your code better. It's not necessarily the case that a const or immutable member variable is exposed in the API. It could be purely internal, or it could be exposed via a property function like any other member variable (I tend to think that member variable should never be exposed except for POD structs, since you lose out on flexibility if you do). But by simply making it const or immutable, you avoid certain bugs and potentially get faster code. IMHO, ideally, any variable that's never going to be mutated would be const or immutable, but that's not always possible for a variety of reasons (e.g. it doesn't play well with struct members, and const doesn't work well with all types). - Jonathan M Davis
Feb 03 2016