When Have You Used C++ 'Mutable' Keyword

When have you used C++ 'mutable' keyword?

Occasionally I use it to mark a mutex or other thread synchronisation primitive as being mutable so that accessors/query methods, which are typically marked const can still lock the mutex.

It's also sometimes useful when you need to instrument your code for debugging or testing purposes, because instrumentation often needs to modify auxiliary data from inside query methods.

Does the 'mutable' keyword have any purpose other than allowing the variable to be modified by a const function?

It allows the differentiation of bitwise const and logical const. Logical const is when an object doesn't change in a way that is visible through the public interface, like your locking example. Another example would be a class that computes a value the first time it is requested, and caches the result.

Since c++11 mutable can be used on a lambda to denote that things captured by value are modifiable (they aren't by default):

int x = 0;
auto f1 = [=]() mutable {x = 42;}; // OK
auto f2 = [=]() {x = 42;}; // Error: a by-value capture cannot be modified in a non-mutable lambda

Why is Mutable keyword used

It allows you to write (i.e. "mutate") to the rno member through a student member function even if used with a const object of type student.

class A {
mutable int x;
int y;

public:
void f1() {
// "this" has type `A*`
x = 1; // okay
y = 1; // okay
}
void f2() const {
// "this" has type `A const*`
x = 1; // okay
y = 1; // illegal, because f2 is const
}
};

Is this a good use-case of mutable in C++?

For the code you showed, you don't need to use mutable keyword at all.

In a const member function, the this pointer is const-qualified, then the members will be const too, for m_buffer it will be int* const (note it's not const int*), it means you still could change the values pointed by m_buffer, but not itself.

And in evaluate() you're only changing the valus m_buffer points to, not m_buffer itself, so it should be fine.

Why does C++11's lambda require mutable keyword for capture-by-value, by default?

It requires mutable because by default, a function object should produce the same result every time it's called. This is the difference between an object orientated function and a function using a global variable, effectively.

Is the c++ mutable keyword bad for threads?

So is it pointless or bad to use C++'s mutable keyword?

No; the mutable keyword is A Good Thing. mutable can be used to separate the observable state of an object from the internal contents of the object.

With the "cached data" example that you describe (a very common use of mutable), it allows the class to perform optimizations "under the covers" that don't actually modify the observable state.

With respect to accessing an object from multiple threads, yes, you have to be careful. In general, if a class is designed to be accessed from multiple threads and it has mutable variables, it should synchronize modification of those variables internally. Note, however, that the problem is really more a conceptual one. It's easy to reason that:

  1. All of my threads only call const member functions on this shared object
  2. Const member functions do not modify the object on which they are called
  3. If an object is not modified, I don't need to synchronize access to it
  4. Therefore, I don't need to synchronize access to this object

This argument is wrong because (2) is false: const member functions can indeed modify mutable data members. The problem is that it's really, really easy to think that this argument is right.

The solution to this problem isn't easy: effectively, you just have to be extremely careful when writing multithreaded code and be absolutely certain that you understand either how objects being shared between threads are implemented or what concurrency guarantees they give.

Correct alternative to a 'mutable function' in c++

make run() const, and make m_data and m_completed mutable. This works but is conceptually wrong because run() demonstrably changes data.

Not true, actually. The variables within your class are, in fact, altered but you could never, ever demonstrate this. Calling run() does not change anything that the user is able to retrieve from your class's interface. If you can't retrieve any information about such a change then you can't possibly demonstrate that change. This is more than a semantic issue, it speaks directly to the whole point of the 'mutable' keyword.

The 'mutable' keyword is greatly misunderstood.

That said, though with the minimally given information I have I might do it the above way, I'm not recommending it. There's almost certainly a better method that would be apparent given a larger view of your problem.

The other method I might use is what you're apparently set on avoiding: force the user to call run() before using get_data(). To tell the truth though, this is a really suboptimal method too. Perhaps more so.

Edit:

If you do decide to use the mutable method then I'd suggest some changes. Having a function called 'run()' that is const and returns nothing of interest would be quite confusing. This function should certainly be non-const. Thus, what I would do, given a decision to do it this way already, is have run() call a const and private function that has the behavior of the current 'run()' function, which is also referred to by get_data() under the specified conditions.

Is it correct to specify class-member mutex 'mutable' for the purpose of much-more 'const' member functions?

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 concurrent const member functions.

The linked article has many code examples and further elaboration, should you want to dive deeper.



Related Topics



Leave a reply



Submit