3

today I have learned about the mutable keyword in C++ and would like to use it in my code.

I have a class with many const methods and one of them should be able to modify some of the object's variables (conserving the logical state of the object). However I don't want to let all the const methods to modify the variable, only the selected one. Is there any way of doing that? Maybe with const_cast?

(The code I am talking about is an implementation of the Union-Find structure. The Find operation does not change the logical state of the structure (it only searches for a root of a tree), but changes the physical state by doing so-called path compression)

Thanks!

EDIT: I have added an excerpt from the code I am referring to:

class UnionFind {
  public:
    void Union(int a, int b) {...}

    int Find(int x) const {
      // logically, this method is const
      while(x != parents[x]) {
        // path compression
        // the next three lines modify parents and sizes, 
        // but the logical state of the object is not changed
        sizes[parents[x]] -= sizes[x];
        sizes[parents[parents[x]]] += sizes[x];
        parents[x] = parents[parents[x]];

        x = parents[x];
      }
      return x;
    }

  int someOtherMethodThatAccessesParents() const {
    // this method does access parents, but read only.
    // I would prefer if parents behaved like if it was 
    // not 'mutable' inside this method
    ...
  }

  private:
    // these have to be mutable if I want the Find method
    // to be marked const (as it should be)
    // but making them mutable then does not enforce 
    // the physical non-mutability in other const methods :(
    mutable std::vector<int> parents;
    mutable std::vector<int> sizes;
 };
serycjon
  • 510
  • 5
  • 14
  • 2
    Learn about physical vs logical constness http://stackoverflow.com/a/3830484/151641 – mloskot Dec 13 '16 at 17:56
  • 4
    If you declare a member mutable, it may be modified from any of the class's methods. You should declare it private, so subclasses can't access it, and rely on good engineering practice to enforce your rule. If your code reviews can't pick up such misuses, it suggests your class might be too big. – Toby Speight Dec 13 '16 at 18:12
  • 1
    I'm pretty sure the only time i've ever used mutable in production code is to mark a `std::mutex` as mutable so that const methods can prevent changes in state while they are running. – Richard Hodges Dec 13 '16 at 18:56
  • 4
    _"I have learned about the mutable keyword in C++ and would like to use it in my code."_ - Are you sure this is the best choice? You just learned about a new feature but that's a far cry from knowing how to use it effectively or what situations its use would be considered idiomatic (or not). Before using this wonderful feature I suggest you learn a little more about effectively using it otherwise you're likely going to run into problems later on. – Captain Obvlious Dec 13 '16 at 19:05
  • @CaptainObvlious, well I can choose between a code without const methods and this, so I am pretty sure this is a better choice. The point of this question is to learn more about using it effectively, thanks for suggestion anyways. – serycjon Dec 13 '16 at 22:07
  • well after read your code it's seem to me that Find (you should remove this upper case) should not be `const`. `mutable` exclude the member variable of `const` qualifier. ["C++ is only bitwise const"](http://stackoverflow.com/questions/105014/does-the-mutable-keyword-have-any-purpose-other-than-allowing-the-variable-to#comment23012_105061). You can't do what you ask. – Stargateur Dec 13 '16 at 23:03
  • @Stargateur (yeah, should fix my naming conventions). Based on [isocpp](https://isocpp.org/wiki/faq/const-correctness), I have got the idea that a logically `const` method should be marked as `const` (quote: Conversely, a method is logically an inspector and should be `const` if it never changes any part of the object’s logical state, even if (as actually happens!) the method changes physical bits of the object’s concrete state.), so this is not true? I am getting more and more lost :( – serycjon Dec 13 '16 at 23:20
  • @serycjon No, isocpp is right but they are very strict. I just say that what you ask is impossible in cpp for the moment. You can let what you write. There are only 2 solution, `mutable` or remove `const`. I let you choice. – Stargateur Dec 14 '16 at 07:32
  • @Stargateur: Hmm :(. Could you write this as an answer, so that I could accept it? So far your comments seem to me to be the best answer to my question. Thanks! – serycjon Dec 14 '16 at 17:14

2 Answers2

4

On first glance this can't be achieved unless you use a nasty const_cast. But don't do that since the behaviour on attempting to modify a variable following a const_cast that was originally declared as const is undefined.

However, it might be feasible to achieve what you want using friendship since that can be controlled on a function by function basis whereas mutability, as you correctly point out, cannot be.

Put the variable you want to modify in a base class and mark it private. Perhaps provide a "getter" function to that member. That function would be const and would probably return a const reference to the member. Then make your function a friend of that base class. That function will be able to change the value of that private member.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • This answers the question as asked. Of course, one concern is that other member functions of the (derived) class will be unable to even access the value of the mutable member unless they are also declared as friends. That may defeat the purpose of the member being mutable in the first place – Peter Dec 13 '16 at 18:41
  • @peter: that's why I suggest a const function is provided. Quite a lot of boilerplate I know. – Bathsheba Dec 13 '16 at 18:58
  • @Bathsheba: I have added a code example. I don't fully understand your answer yet, but it seems to me, that the const_cast would work in this case, because the variable was not declared as const? I understand this would be a nasty solution, but the separate class seems to be much more nasty to me. Also does your solution give a zero runtime overhead? Thanks for your help! – serycjon Dec 13 '16 at 22:32
-4

If you can afford to use mutable, that is the right way to do it.

Still, it's possible to do what you are asking for. Normally this is done via the “fake this” idiom:

MyClass *mutableThis = const_cast<MyClass*>(this);

Then access your field normally through the new pointer. This is also the way to do it if you have to support some old compiler with no mutable support.

Note however that this is generally a dangerous practice, as it can easily lead you into the dreaded realm of undefined behavior. If the original object was actually declared const (as opposed to just being accessed via a const pointer/reference), you're asking for trouble.

In short: use mutable when you can, use fake this when you can't, but only when you know what you're doing.

Sergei Tachenov
  • 24,345
  • 8
  • 57
  • 73
  • 2
    I was the downvoter. It appears you are encouraging undefined behavior. OP has stated that their const function needs to modify some variables, so casting the `const` away from `this` within a `const` function, and then modifying a member would be undefined. – AndyG Dec 13 '16 at 18:00
  • 2
    @AndyG strictly speaking, it's only UB if the modified variable/object was originally declared as `const`. – Jesper Juhl Dec 13 '16 at 20:08
  • 2
    @JesperJuhl: True, I concede as much. I guess I'll downgrade my comment to "This is dangerous and usually indicative of bad design." It will blow up as soon as you have a method receiving a `const MyClass& _instance` that attempts to call your "const" function. `mutable` is the is the correct approach for modifying the physical but not logical state. Otherwise, remove the `const` because it's misleading. – AndyG Dec 13 '16 at 20:21
  • @AndyG, while I agree with everything you said, this approach was actually encouraged by [Mozilla's C++ Portability Guide](http://www.literateprogramming.com/portablecpp.pdf). It's no longer there in the current version, which probably means that modern compilers finally support this thing widely enough. Still, it's a valid approach if you know what you're doing. I'll keep the answer despite reputation drop, but I should probably edit it to properly warn about the dangerous practice. – Sergei Tachenov Dec 14 '16 at 06:09