www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - const and immutable member variables in classes

reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
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
next sibling parent reply anonymous <anonymous example.com> writes:
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
parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
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
prev sibling next sibling parent Jonathan M Davis via Digitalmars-d-learn writes:
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
prev sibling next sibling parent "H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> writes:
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:
[...]
 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.
[...] 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 -- Любишь кататься - люби и саночки возить.
Feb 02 2016
prev sibling parent reply Jonathan M Davis via Digitalmars-d-learn writes:
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:
 On Tuesday, February 02, 2016 14:48:03 Ali ehreli via Digitalmars-d-learn
wrote:
 const and immutable members make structs non-assignable:
[...]
 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.
[...] 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.
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 Davis
Feb 02 2016
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
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 be 
initialized on
 construction and then never mutated, having it be const or immutable 
would
 be 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, 
because
 you 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
next sibling parent Jonathan M Davis via Digitalmars-d-learn writes:
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
prev sibling parent reply "H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> writes:
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:
 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.
[...] 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, K5
Feb 02 2016
parent Jonathan M Davis <jmdavisProg gmx.com> writes:
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