Question
C++ mutable Keyword Explained: Const Member Functions, Caches, and Mutexes
Question
In C++, what is the purpose of the mutable keyword beyond allowing a data member to be modified inside a const member function?
For example:
class Foo {
private:
mutable bool done_;
public:
void doSomething() const {
// ...
done_ = true;
}
};
My understanding is that mutable allows a member variable to be changed even when the containing object is treated as const.
I have also seen this used with a boost::mutex so that const member functions can still lock the mutex for thread safety. That seems useful, but it also feels a little unusual.
Is this essentially the only role of mutable, or are there other important uses and design reasons behind it?
Short Answer
By the end of this page, you will understand what mutable means in C++, why it exists, and when it is appropriate to use it. You will see that its main job is to allow changes to an object's internal, non-observable state even inside const member functions. Common examples include cached values, lazy computation, counters, and mutexes used for thread safety.
Concept
In C++, mutable is a keyword that can be applied to a non-static data member of a class. It tells the compiler:
"This member is allowed to change even when the object is viewed through a
constinterface."
That is the core purpose of mutable.
Why this exists
A const member function promises not to change the object's logical state from the caller's point of view. However, some parts of an object are often considered implementation details rather than part of its visible value.
Examples:
- a cached result
- a lazily initialized lookup table
- a debug counter
- a mutex used only for synchronization
These do not usually change what the object means; they only support how it works internally.
This leads to an important C++ idea:
Physical constness vs logical constness
- Physical constness: no bits in the object change.
- Logical constness: the externally visible meaning of the object does not change.
const member functions are mainly about logical constness in good design. mutable exists so you can preserve a const API while still changing internal bookkeeping data.
Mental Model
Think of an object as a library book.
- The content of the book is its logical state.
- A sticky note inside the cover is internal bookkeeping.
If a librarian adds a sticky note saying, "last checked on Tuesday," the story in the book has not changed. The book is logically the same book.
In this analogy:
- normal data members are part of the book's content
mutablemembers are the sticky notes used for internal management- a
constmember function means "do not change the meaning of the book"
So mutable says:
"This field is internal housekeeping. It may change without breaking the promise of
const."
Syntax and Examples
The syntax is simple: place mutable before a non-static data member.
class Example {
private:
int value_ = 0;
mutable int cache_ = 0;
};
Basic example
#include <iostream>
class Number {
private:
int value_;
mutable bool inspected_ = false;
public:
Number(int value) : value_(value) {}
int getValue() const {
inspected_ = true; // allowed because inspected_ is mutable
return value_;
}
};
int main() {
const Number n(10);
std::cout << n.() << ;
}
Step by Step Execution
Consider this example:
#include <iostream>
class Temperature {
private:
double celsius_;
mutable bool cacheValid_ = false;
mutable double cachedFahrenheit_ = 0.0;
public:
Temperature(double celsius) : celsius_(celsius) {}
double fahrenheit() const {
if (!cacheValid_) {
cachedFahrenheit_ = celsius_ * 9.0 / 5.0 + 32.0;
cacheValid_ = true;
}
return cachedFahrenheit_;
}
};
int main() {
const Temperature t(25.0);
std::cout << t.fahrenheit() << "\n";
std::cout << t.fahrenheit() << "\n";
}
Step-by-step
First call to
Real World Use Cases
mutable appears in real C++ code when a class has internal details that may change without changing its public meaning.
Common use cases
Caching expensive calculations
- computing a parsed representation once
- storing the result of a lookup
- memoizing string length, hash, or derived data
Lazy initialization
- creating helper objects only when first needed
- initializing a compiled regex on demand
- building a lookup table the first time a method is called
Thread safety in read-only operations
- locking a mutex inside
constgetters - protecting shared state while preserving a
constAPI
Logging and metrics
- counting accesses
- tracking cache hits and misses
- collecting debug information
Example scenarios
- A
Documentclass caches word count in aconst wordCount()method. - A
Configclass lazily parses a file the first time a getter is called. - A
DatabaseConnectionPooluses amutable std::mutexinconstmethods to safely inspect pool state.
Real Codebase Usage
In real projects, mutable is usually used sparingly and intentionally.
Common patterns
1. Cached values in getters
Developers often keep the public API read-only while storing computed results internally.
class UserProfile {
private:
std::string rawJson_;
mutable bool parsed_ = false;
mutable std::string displayName_;
public:
std::string displayName() const {
if (!parsed_) {
// parse rawJson_...
displayName_ = "Alice";
parsed_ = true;
}
return displayName_;
}
};
2. mutable mutex for const-correct locking
This is very common and generally considered good practice.
class Store {
private:
std::vector<int> items_;
mutable std::mutex mutex_;
public:
std::size_t size {
;
items_.();
}
};
Common Mistakes
Beginners often misunderstand mutable as a way to "get around" const. That leads to poor design.
Mistake 1: Using mutable to modify real object state
Broken idea:
class BankAccount {
private:
mutable int balance_;
public:
void withdraw(int amount) const {
balance_ -= amount; // very misleading design
}
};
Why this is bad
Withdrawing money clearly changes the logical state of the object. A const function should not do that.
Fix
Make the function non-const.
void withdraw(int amount) {
balance_ -= amount;
}
Mistake 2: Thinking mutable makes code thread-safe
Broken idea:
Comparisons
Here is how mutable compares to related ideas.
| Concept | What it does | Typical use | Changes logical state? |
|---|---|---|---|
const member function | Promises not to modify the object's logical state | getters, read-only operations | No |
mutable member | Allows one specific member to change in const functions | caches, mutexes, counters | Usually no |
non-const member function | Freely modifies object state | setters, updates, mutations | Yes |
const_cast | Removes constness manually | rare low-level cases |
Cheat Sheet
mutable Type member;
Rules
mutableapplies to non-static data members.- It allows that member to be modified inside
constmember functions. - It does not make other members modifiable.
- It is mainly used for logical constness.
Good uses
- caches
- lazy initialization
- mutexes for synchronization in
constmethods - debug counters or metrics
Bad uses
- changing true business state in a
constmethod - hiding poor class design
- assuming it provides thread safety
Example
class Example {
private:
int value_ = 0;
mutable std::mutex mtx_;
public:
int get() const {
std::lock_guard<std::mutex> lock(mtx_);
value_;
}
};
FAQ
What does mutable mean in C++?
It means a specific non-static data member can still be modified even when the object is accessed through a const member function.
Is mutable only used with const member functions?
That is its main purpose. It matters when an object is treated as const, especially inside const member functions.
Is using mutable for a mutex good practice?
Yes, usually. Locking is considered internal synchronization, not a change to the object's logical value.
Does mutable break const-correctness?
Not if used properly. It supports logical constness by allowing internal implementation details to change.
Can mutable be used for caching?
Yes. Caching is one of the most common and appropriate uses.
Does mutable make code thread-safe?
No. It only changes const rules. You still need mutexes, atomics, or other synchronization tools.
Should I use mutable often?
Usually no. It is useful, but it should be used sparingly for clearly internal state.
Mini Project
Description
Build a small C++ class that stores a piece of text and exposes const methods to inspect it safely and efficiently. The project demonstrates two common uses of mutable: caching a computed value and locking a mutex inside a const method.
Goal
Create a thread-safe TextStats class with a const method that returns a cached word count.
Requirements
- Create a class that stores a text string.
- Add a
constmethod that returns the number of words. - Cache the computed word count so repeated calls do not recalculate it.
- Use a mutex to protect access to shared state.
- Keep the public read operation
const.
Keep learning
Related questions
Advantages of Brace Initialization in C++
Learn why C++ brace initialization is often clearer and safer than other object initialization styles, with examples and common pitfalls.
Basic Rules and Idioms for Operator Overloading in C++
Learn the core rules, syntax, and common idioms for operator overloading in C++, including member vs non-member operators.
C++ Aggregates, Trivial Types, Trivially Copyable Types, and PODs Explained
Learn what aggregates, trivial types, trivially copyable types, and PODs mean in C++, how they differ, and why they matter.