When const Isn't const: A Deep Dive into C++ mutable
C++ mutable keyword

When const Isn't const: A Deep Dive into C++ mutable

In C++, mutable is used to modify class member variables from const member functions. By default, member variables of a class cannot be modified from a const member function.

class MyClass 
{
  int x; 
  mutable int y;

  void constFunc() const {
    x = 10; // Error - cannot modify from const function
    
    y = 20; // Okay - y is mutable
  }

};        

It is typically used for class member variables that cache calculated values. You want to declare the class object const so the state cannot change, but need to modify cached values.

Example:

A const object that caches calculated values that are expensive to compute. The cached values can be declared mutable so they can be updated even though the object is const.

class MyClass 
{
  public: 
    MyClass() : cached_value(computeExpensiveValue()) {}

  private:
    int computeExpensiveValue() 
    {
      // long computation here
      return result;
    }
  
  public:
    mutable int cached_value;
};

const MyClass obj; // object is const
obj.cached_value = 10; // this is allowed as cached_value is mutable        

We use mutable when we want some variables to be modifiable even when accessed through a const reference/pointer to an object. This is useful for things like caching, logging, etc.

const methods cannot modify normal (non-mutable) members. But const methods can modify mutable members.

class MyClass
{
  public:
    mutable int x;
    
    void mutateX() const {
      x = 100; // OK, mutates mutable member
    }
};

const MyClass obj; 
obj.mutateX();
// obj is const, but mutateX() can still change x since x is mutable        

The mutable keyword essentially overrides the constness of a specific member variable. Other member variables will still be const as expected.

Important facts

  • It can unintentionally break the immutability of const object.
  • Usage should be restricted where performance gains are substantial and it makes logical sense. Appropriate comments explaining mutable usage are helpful.
  • Only member variables can be declared as mutable, not local variables or parameters.
  • The mutable keyword does not work on const objects declared immutable at compile time using constexpr.
  • Mutable allows modification of the variable, but not changing its address. So pointers or references declared as mutable cannot be made to point to different objects.

class MyClass 
{
private:
  int* ptrData;

public:
  void setDataPointer(int* data) const
  {
    ptrData = data;
  }
};

const MyClass obj;
int* newData = new int[100];
obj.setDataPointer(newData); // error        

If "MyClass" is const, you cannot call setDataPointer() to change where ptrData points to, since that would mutate the class.

But if you declare ptrData as mutable, you can change what ptrData points to, but not ptrData itself.

class MyClass 
{
private:
  mutable int* ptrData;

public:
  void setDataPointer(int* data) const
  {
    ptrData = data;
  }
};

 const MyClass obj;
 int* newData = new int[100];
 obj.setDataPointer(newData); // can be done

 int** ptrToPtr = &obj.ptrData;
 *ptrToPtr = &newData; // Cannot change ptr itself        

  • Using mutable breaks the logical const correctness of the class from an external perspective. The class can be mutated even if const, which can be confusing.

class MyClass 
{
public:
  mutable int* ptrData;

public:
  void setDataPointer(int* data) const
  {
    ptrData = data;
  }
};

MyClass obj;
int* newData = new int[100];
obj.setDataPointer(newData); // error        

  • Any thread safety guarantees on a const class are broken if it uses mutable since the state can now change.
  • Overuse of mutable can degrade encapsulation and modularity as implementation details "leak" out.
  • The purpose and need for mutability should be well documented to avoid confusion.
  • Mutable usage defeats the compiler's ability to optimize based on assumptions about unchanged state.

When compiler sees a variable or object declared const, it is able to make certain performance optimizations based on the assumption that the state will not change.

// obj is declared const
const MyClass obj;

// Access member multiple times
obj.x; 
obj.x;
obj.x;        

Here, the compiler can optimize to cache the value of obj.x if it knows it won't change. It may even optimize away multiple reads to just read it once.

If we declare x as mutable;

class MyClass 
{
  mutable int x;
}

const MyClass obj;

obj.x;
obj.x; // may not be optimized        

Now, compiler cannot make any assumptions about obj.x staying the same between two reads. It cannot cache or optimize away reads, because x could have been changed by another thread or mutable member function.

FAQ: Why static class members cannot be declared as mutable?

  • Mutable is used to modify class member variables in a const object.
  • However static members are associated with the class type, not individual objects.
  • So, mutable doesn't apply to static members as they aren't tied to an object's state.

FAQ: Can we declare a function as mutable?

  • No, only member variables can be declared as mutable, not functions.
  • Declaring a function mutable would not be meaningful.

FAQ: Can we declare an object as mutable?

  • No, mutable is used on member variables of a class, not objects themselves.
  • Making an entire object mutable implicitly defeats the purpose of immutability.

FAQ: Why there thread safety broken?

Let's understand using an example.

class MyClass 
{
  mutable int x; 
  ...
};

const MyClass obj1; 

// Thread 1
obj1.x = 10; 

// Thread 2 
obj1.x = 20; // Works as x is mutable!        

  • Here two threads are able to modify the state of a const object due to mutable. This breaks thread safety expectations.

FAQ: Why do any thread-safe guarantees on a const class get broken if it uses mutable?

  • const objects are assumed thread-safe as their state cannot change.
  • But mutable lets state change even in a const object.
  • So any assumptions of thread safety for const no longer hold if mutable is used.
  • Multiple treads may mutable state in unpredictable ways.






要查看或添加评论,请登录

Lahiru Dilshan的更多文章

社区洞察

其他会员也浏览了