Mutable and The M&M Rule in C++ (ENG)

Do this at first: Const in C++

Mutable and Constness

The mutable specifier allows a member to be modified in a const member function. In the example below, we qualify a function with const, which means we are not supposed to change the class members in the function. However, we do modify the mutable count member within the function scope.

class myClass{
private:
	int variable = 0;
	mutable int count;
public:
	int getVar() const{
	    count++;
	    return variable;
    }
};
int main(){
    myClass obj;
    int value = obj.getVar();
    return 0;
}

"Const as a promise", using mutable specifier can break the constness of a function, which can be seen as poor code design. The principal of "const as a promise" implies that a const member function should not modify any member variables. But mutable is an exception to this rule, which can lead to unexcepted behavior.

However, mutable could be useful in some situations like mutexes for safer code(the M&M rule), cache memory, and lazy evaluation.

The M&M Rule: Mutable and Mutex go Together

This code example is copied from cppreference. As you can see, we have a const get() function, and with std::mutex, we create safe concurrent code. Why is that? While we are not supposed to modify anything in the const specified functions, with mutable, another thread can actually modify the data in a const specified function. This is no thread-safe.

class ThreadsafeCounter
{
    mutable std::mutex m; // The "M&M rule": mutable and mutex go together
    int data = 0;
public:
    int get() const
    {
        std::lock_guard<std::mutex> lk(m);
        return data;
    }
 
    void inc()
    {
        std::lock_guard<std::mutex> lk(m);
        ++data;
    }
};

For achieving the goal of safer code and const correctness at the same time, we have to use the mutable keyword to make the mutex mutable in the const function. While a thread is modifying the critical section, using a lock to prevent other threads from entering and accessing the resource is thus necessary.