08 - mutable

Expensive getters

Consider a situation where there is a class that performs lots of calculations and the result takes some time to compute.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class simulation
{
public:
	bool set_amount(int value)
	{
		if (value < 1)
			return false;

		amount = value;
		return true;
	}

	bool set_time(double ms)
	{
		if (value < 0.0)
			return false;

		time_ms = ms;
		return true;
	}

	bool set_velocity(double value)
	{
		velocity = value;
		return true;
	}

	// more setters...

	simulation_result compute_result() const; // very expensive

private:
	int amount;
	double time_ms;
	double velocity;
	// more data members...
};

So far everything looks fine:

  • all fields are private

  • setters secure invariants

  • computation function is const-qualified

But there is one problem - we know that data doesn't change often but we often need the computation result. If we call this function over and over, we are continuously repeating the same (expensive) calculations. We could implement some caching so that if data has not changed the class just returns the last result.

We can add a result field to the class to store last result inside but now we have a problem - the const-qualified function to compute the result can not change it!

mutable members

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
class simulation
{
public:
	bool set_amount(int value)
	{
		if (value < 1)
			return false;

		if (value != amount)
		{
			amount = value;
			last_result = std::nullopt;
		}

		return true;
	}

	bool set_time(double ms)
	{
		if (value < 0.0)
			return false;

		if (ms != time_ms)
		{
			time_ms = ms;
			last_result = std::nullopt;
		}

		return true;
	}

	bool set_velocity(double value)
	{
		if (value != velocity)
		{
			velocity = value;
			last_result = std::nullopt;
		}

		return true;
	}

	// more setters...

	simulation_result get_result() const
	{
		if (!last_result)
			last_result = compute_result();

		return last_result.value();
	}

private:
	simulation_result compute_result() const;

	int amount;
	double velocity;
	double time_ms;
	// more data members...

	mutable std::optional<simulation_result> last_result;
};

We have moved actual computation to a private function, which is now called when there is no result available. Setters have been modified to reset the result if specified parameter values are different. Now the code outside the class can call get_result as many times as needed and it will only be computed if something changed.

In practice

In practice mutable is used very sparingly. It's purpose is to specify that the member does not affect the externally visible state of the class. It should be used when there is a strong desire to const-qualify a member function but specific class implementation (such as result caching in the presented example) requires changes in some members in such function.

The most common use of mutable is for members that implement synchronization mechanisms such as std::mutex. For a thread-safe class, a mutex needs to be locked and unlocked in every member function so the only way to preserve const-correctness on the outside is to use mutable std::mutex member.

Extra technical details
  • mutable fields can not be const on the top level

  • mutable fields can not be static

  • mutable fields can not be references

1
2
3
4
5
6
7
8
9
struct test
{
	mutable       int* p1;       // ok
	mutable const int* p2;       // ok
	mutable       int* const p3; // error
	mutable const int* const p4; // error
	mutable int&       r;        // error
	mutable static int s;        // error
};