- References
- Function Overloading
- Default Function Arguments
- Inline Function
- Dynamic Memory Allocation
- Placement New
- nullptr
- Namespaces
- Type Aliases
- Enum classes
- constexpr
- static_assert
- mutable Keyword
- auto Keyword
- Smart Pointers
- Basics of Classes
- Constructors
- Destructors
- Operator Overloading
- Copy Semantics
- Move Semantics
- Composition, RAII & Ownership
- Inheritance
- Polymorphism
- Abstraction
- Encapsulation
- Template
- Static Memory
- Friend Function
- this Pointer
- Function Pointer
- Lambdas and Callback Management
- Union
mutable Keyword

In C++, a const member function (e.g., void read() const) promises not to modify any member variables of the object. The compiler enforces this strictly.
However, sometimes you need to modify a variable that does not affect the logical state of the object, such as a mutex lock, a usage counter, or a cached value.
The mutable keyword allows a specific member variable to be modified even inside a const function.
Syntax & Usage
1. Basic Usage (The Cache Example)
Imagine a sensor class where reading is const (logical state doesn't change), but you want to update a "last read timestamp" for debugging.
class Sensor {
private:
int pin;
mutable int last_access_time; // Can be changed in const functions
public:
Sensor(int p) : pin(p), last_access_time(0) {}
int read() const {
// We are reading hardware, so logical state is const.
// But we want to update the debug timestamp.
last_access_time = get_system_time(); // ✅ Allowed because of 'mutable'
return hardware_read(pin);
}
};2. Thread Safety (Mutexes)
This is the most common real-world use case. Locking a mutex changes the mutex's internal state, but it doesn't change the logical state of the data it protects.
class DataStore {
mutable std::mutex mtx; // Mutex must be mutable
int data;
public:
int get_data() const {
std::lock_guard<std::mutex> lock(mtx); // ✅ Modifies mtx, but that's okay
return data;
}
};Logical vs. Bitwise Constness
| Bitwise Const (Default) | Logical Const (With mutable) |
|---|---|
| Compiler checks strictly. | Developer promises logical consistency. |
| No bits in the object can change. | Some bits (caches/locks) change, but the object "looks" the same. |
| Good for simple data structs. | Essential for complex objects with internal management. |
Relevance in Embedded/Firmware
1. Thread-Safe Getters
In firmware, you often have get_status() functions that must be thread-safe. To make them thread-safe, you need to lock a mutex or disable interrupts. Since get_status() should logically be const, you mark the mutex/lock as mutable to allow locking without removing the const qualifier from the API.
2. Lazy Initialization / Caching
Embedded systems often delay expensive calculations until needed.
class Config {
mutable int cached_crc;
mutable bool crc_valid = false;
public:
int get_crc() const {
if (!crc_valid) {
cached_crc = calculate_expensive_crc(); // Modifies state lazily
crc_valid = true;
}
return cached_crc;
}
};Common Pitfalls (Practical Tips)
| Pitfall | Details |
|---|---|
| ❌ Overuse | Don't use mutable just to cheat the compiler because you're too lazy to fix your design. Only use it for things that truly don't affect the object's "value." |
| ❌ Thread Safety Confusion | Just because a variable is mutable doesn't make it thread-safe. You still need locks. |
| ✅ Debug Counters | It is excellent for internal debug counters (e.g., mutable int access_count;) that track how many times a const function was called without changing the function signature. |
Concept understood? Let's apply and learn for real