mutable Keyword

cardimg

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)

PitfallDetails
❌ OveruseDon'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 ConfusionJust because a variable is mutable doesn't make it thread-safe. You still need locks.
✅ Debug CountersIt 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

Practice now