C++ : mutable
mutable
class member variable
Class member variables marked as mutable
can be updated with a const reference to the class object.
The idea reminds me of the "Interior Mutability Pattern"
in Rust.
Use mutable
to collect runtime data access stats.
class DataWrapper {
SuperImportantDataClass data;
mutable unsigned refCnt = 0; // Track how many times this data is read.
}
void ReadOnlyTask(const DataWrapper &obj) {
/*
This function takes const reference of `DataWrapper`.
Do read-only work with inner data.
*/
// Update member variable with const reference, thanks to `mutable`.
obj.refCnt += 1;
}
With such use of mutable
, you can get insight into the data access patterns of your program.
You can also write your code to do useful stuff at runtime according to the data access stats collected at runtime
(e.g. adjust storage locations of a data item depending on its popularity).
Use mutable
to implement per-item atomics
#include <chrono>
#include <mutex>
#include <thread>
class DataWrapper {
SuperImportantDataClass data;
mutable std::mutex lock;
}
void ReadOnlyTask(const DataWrapper &obj) {
std::chrono::milliseconds interval(100);
while (true) {
// Can lock `obj.lock` with const reference, thanks to `mutable`.
if (obj.lock.try_lock()) {
/*
Do read-only work with inner data.
*/
} else {
std::this_thread::sleep_for(interval);
}
}
}
Here I used std::mutex
for convenience, but you can instead add complex mutable state within DataWrapper
to implement your own locking scheme.
Since ReadOnlyTask
takes a const reference, it’d be more reasonable to replace std::mutex
with a reader-writer lock.
Storing lock
alongside the guarded data takes advantage of cache locality. That’s probably why Rust’s mutex, std::sync::Mutex
, encloses the guarded data and is located alongside it.
mutable
on lambda
This stackoverflow Q&A provides enough context for mutable
on lambdas.
// C++ example
#include<string>
void foo() {
std::string s("Hello World")
// OK
[=]() mutable { s.push_back('!') ;}();
// Error: a by-value capture cannot be modified in a non-mutable lambda
// [=]() { s.push_back('!') ;}();
// s == "Hello World"
}
// Imitate above C++ lambda example with Rust closures:
fn foo() {
let s = String::from("Hello World");
let s1 = s.clone();
(move || {
let mut s = s1; // Move the cloned string to within closure.
s.push('!');
})();
// s == "Hello World"
}