tags:

views:

610

answers:

7

I would like to ask if the use of mutable is appropriate here:

#include <iostream>

class Base
{
protected:
  int x;

public:
  virtual void NoMod() const
  {
    std::cout << x << std::endl;
  }
  void Draw() const
  {
    this->NoMod();  
  }
};

class Derive : public Base
{
private:
  mutable int y;

public:
  void NoMod() const
  {
    y = 5;
  }
};

int main()
{
  Derive derive;

  // Test virtual with derive
  derive.Draw();

  return 0;
}

The Base class is a 3rd-party library. I'm extending it to provide my own NoMod(). The library original NoMod() is declared as a const.

My NoMod() differs from Base in the fact that it needs to modify its own member variable.

Thus, for my own NoMod() to compile and get called when Draw() is called, I had to

1) Implement Derive::NoMod() as a const
2) make my int y mutable.

Is this the best I can do?

+3  A: 

The only time I think mutable is okay is for things like reference counts that aren't really part of the object's state.

If y is part of the object's physical state, but not logical state, then this is okay, but otherwise, don't do it.

Zifre
+8  A: 

It's hard to say, since you don't give any context on what y refers to or how it's used.

In general, mutable is only appropriate when changing the mutable variable doesn't change the actual "value" of the object. For example, when I was writing a wrapper for C-style strings, I needed to make the internal mLength variable mutable so that I could cache the length, even if the thing it was requested on was a const object. It didn't change the length or the string, and wasn't visible outside of the class itself, so making it mutable was okay.

Head Geek
+1  A: 

Another situation where you can think of 'mutable' is when you have a class with 'const' member variables and you require to implement the assignment operator(=) without skipping the assignment of the 'const' member.

Also, const_cast apllied on originally declared 'const' variable and using it is U.B. according to C++ standard. So if the interface of a method accepts a 'const' which has to be modified internally, pass it a 'mutable' argument.

You may judge the appropriateness of the same from the above situations i.e. only of it makes sense semantically! Do not use it to make the source-code compilable.

Regards,

Abhay
"Also, const_cast apllied on originally declared 'const' variable is U.B. according to C++ standard."It's not the const_cast that invokes undefined behavior, it's const_casting away const-ness and then _modifying_ the object.e.g.const int x = 10;cout << const_cast<int>(x) + 1 << endl; // fine
Logan Capaldo
Yes you are right. But i guess one would cast away constness in order to use it :-)
Abhay
+1  A: 

Use mutable when the member of the class is not really defining the state of the object, (e.g. is a cached value/object that helps improving performance).

I use to do another difference. In your example, you only enforce change to const object only once. You can use also const_cast operator:

const_cast< Derive*>( this)->y = 10;

When you use const_cast operator, you have the advantage that you can easily identify the places where you enforced the const to non-const conversion by simply running a search in your code for the operator name.

However, as I said, if the member is not part of the state of the object, but must be changed indirectly in several constant methods, use mutable for that member.

Cătălin Pitiș
+2  A: 

The only situations where I needed the mutable feature are:

  • a cached version of derived data. For example, if you have a Rectangle class that has a GetSurface() member function that is likely to be called a lot, you could add a mutable m_surfaceCache member variable to keep the derived data.
  • a critical section member variable. This is because my CriticalSection::Enter() function is conceptually not const, but the critical section member variable is not a real part of the class data, it is more like a compiler guideline.

However, as a general rule of thumb, I'dd advise not to use mutable too often, as it bypasses C++'s wonderful const feature.

Dimitri C.
+6  A: 

As 'head geek' described, the answer to your question depends on how your data member is used.

I distinguish two types of data members in a class.

I use the common term 'attribute' to refer to data members that are the logical state or 'value' of the object. Typically attributes are rarely declared as mutable.

I have coined the protologism 'contribute' it denote data members that are simply 'working memory/storage' and that are somewhat divorced from the state of the object. Contributes have no contextual relevance to the user of the object, they exist in the class only to contribute to the maintenance and efficient operation of the object. Contributes are usually declared in the class as mutable and are always private or protected.

For example let's say your object is a linked list, so you have a pointer to the first item in the list. I would consider this pointer a contribute because it does not represent the data in the list. Even if the list is sorted and the pointer is set to the new first item in the list, the user of the list object could care less how the list is maintained. Only that the list data has been modified or not and that the the list is sorted or not is relevant to the user's perspective. Even if you had a booean data member 'sorted' to quickly determine if the list is in a sorted state, that too would be a contribute because it is the list structure itself which imbues the sorted state, the 'sorted' variable member is used simply to efficiently remember the state without having to scan the list.

As another example, if you have a const method that searches the list. Suppose you know that typically the search will return the most recently previously searched for item, you would keep a pointer in your class to such a item so your method can first check if the last found item matches the search key before searching the entire list (if the method does indeed need to search the list and finds an item, the pointer would be updated). This pointer I would consider to be a contribute because it is only there to help speed up the search. Even though the search updates the pointer contribute, the method is effectively const because none of the items' data in the container are modified.

So, data members that are attributes are usually not declared mutable, and data members that contribute to the functioning of an object will usually be mutable.

Roger Nelson
wow, this was a very good answer. very easy to understand...now i totally understand what they mean by "logcal" and "physical". thank you for such a fine answer.
ShaChris23
+1  A: 

If, as you said, it's part of a third party library, you may not have a choice. C++ is at heart a pragmatic language and lets you do what you need to do, even if it may not always be a "best practice".

One thing to note though, is that the third party library is documenting that NoMod should not modify the object by adding that const specifier. By violating that contract, you leave yourself open to possible trouble. If the library in some situations call NoMod multiple times, your derived class better be able to handle that, since a true const method would have no problem with it.

I'd first look for another way to solve the problem, but failing there declare it mutable.

Eclipse