In many cases, many member-functions could be specified 'const' - they don't modify any data-members of the class ...almost: they do lock/unlock the class mutex. Is it a good practice to specify, in that case, the mutex 'mutable'? ...otherwise, it will impossible to specify get_a(), get_b(), get_c(), get_d() 'const'.
--
A mere example. Lets say that 'example' is a thread-safe class and that m_mutex protects its shared resources that get_a(), get_b(), get_c(), get_d() do not modify:
#include <mutex>
class example
{
public:
size_t get_a(void) const {
std::lock_guard lck(m_mutex);
return m_str.length() + a;
};
size_t get_b(void) const {
std::lock_guard lck(m_mutex);
return m_str.length() * 2 + a + b;
};
size_t get_c(void) const {
std::lock_guard lck(m_mutex);
return m_str.length() * 4 + a + b;
};
size_t get_d(void) const {
std::lock_guard lck(m_mutex);
return m_str.length() * 8 + a;
};
void modify_something() {
std::lock_guard lck(m_mutex);
if (m_str.length() < 1000) {
m_str.append(".");
a++;
b++;
}
};
protected:
mutable std::mutex m_mutex;
std::string m_str;
int a = 0, b = 0;
};
Not only is this correct, but also standard practice. A member mutex
is described as "non observably non-const", which means it behaves as a constant to a "user" observer.
Herb Sutter has popularized the M&M rule:
For a member variable, mutable and mutex (or atomic) go together.
The rule's section related to your question states:
For a member variable, mutex (or similar synchronization type) implies mutable: A member variable that is itself of a synchronization type, such as a mutex or a condition variable, naturally wants to be mutable, because you will want to use it in a non-const way (e.g., take a
std::lock_guard<mutex>
) inside concurrentconst
member functions.
The linked article has many code examples and further elaboration, should you want to dive deeper.