Const correctness and mutable

I've been reading up on const and mutable keywords, and I have come up with a use case in my project where using both could apply. So I would like to hear people's opinions on whether or not that would be a good idea.

Let's say I have these following classes (please notice that this code does not compile, but this is intentional):

1
2
3
4
5
#include "Test1.h"

void Test1::change() const {
	bits.set(1, true);
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef TEST1_H_
#define TEST1_H_

#include <bitset>

class Test1 {
private:
	std::bitset<32> bits;

public:
	void change() const;
};

#endif 


1
2
3
4
5
6
7
8
9
10
#include "Test2.h"
void Test2::change() const {
	t.change();
}

int main() {
	Test2 t2;
	t2.change();
	return 0;
}


1
2
3
4
5
6
7
8
9
10
11
#ifndef TEST2_H_
#define TEST2_H_
#include "Test1.h"

class Test2 {
	public:
		void change() const;
	private:
		Test1 t;
};
#endif 


Normally I'd have to remove the const keywords, but I am thinking about adding mutable instead, and here's my reasoning. Neither Test1, nor Test2 really change anything, they just pass that job onto the object within themselves, so it ends up being bitset doing the actual changing. Thus to me, there's not really any change of state for Test1 and Test2, and it would make sense to make them const for that reason. But then I would have to use mutable.

So I'd like to hear some people's opinion on that reasoning and approach. And if I should do this, where exactly do I place the mutable keyword?
mutable, like friend, has instances where it can be very useful, but it is also very easy to abuse. It's hard to say from this example whether or not using it here is a good idea.

My first question would be... if 'bits' is not part of Test1's state, then why is it a member?


Typically members can be mutable when changing them does not cause any externally visible change to the object. IE, the only code that could possibly be aware that anything has changed is the class itself. Any outside code would continue to see it the object the same as it were if the mutable were not modified.


Though even in those cases, adding mutable members can be hazardous. In particular, it severely damages the thread safety of a class.
So I'd like to hear some people's opinion on that reasoning and approach.


An object contained by a class is part of it's state if changing the class has a visible effect on the class. Here, you show one method of Test2 in isolation, but if you call change and afterwards any other method of Test2's behavior or return value would be visibly different to code outside the class, then change should not be const.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <map>
#include <string>

class Dictionary
{
    std::map<std::string, std::string> _dictionary ;

public:
    
    bool add(const std::string& word, const std::string& definition) ;

    bool contains(const std::string& word) const ;

};


Here, the method add should not be const, despite the fact that the contained map is going to be doing all of the work. It's state clearly changes. Before adding a word, contains returns false for that word. After adding a word, contains returns true for that word. The state of the object has changed.

Viewing single methods in isolation doesn't provide enough context to make an informed opinion.
Well, there is only one method because that was the simplest code I could come up with to illustrate my problem and reasoning.

bits is not part of Test1's state because Test1 is merely a container for a bitset. And Test2, while I didn't think of it being relevant to add, is in fact a friend of Test1. The purpose here then is that I want to ensure that the bitset can only be changed by the Test2 class, and since I can't add friend to the bitset definition, I need a container for it. Plus Test1 is meant to be effectively immutable, so I can make some changes to it through Test2 only, and when I'm done with that, I would have a get method to return the Test1 instance within the Test2 class.

The bitset is also, as in the example, set to a fixed length which I know at compile time, so the only change happening will be the setting of bits, no replacement of it with a new bitset, or changing of the length or anything, and since setting bits is bitset's responsibility, it seems like only its set method should be non-const, as it is.
bits is not part of Test1's state because Test1 is merely a container for a bitset.

That might be like saying _dictionary is not part of Dictionary's state because Dictionary is merely a container for a map. As I said before, viewing single methods in isolation doesn't provide enough context to make an informed opinion. Provide more context, not more rationalization.

The purpose here then is that I want to ensure that the bitset can only be changed by the Test2 class, and since I can't add friend to the bitset definition, I need a container for it.

If your purpose is to make sure that no instance of a std::bitset may be affected by any object that is not of type Test2, then you've failed. If your purpose is to make sure that no object that is not a Test2 object may manipulate the bitset that is a member of Test2, make it a private variable. mutable/const is entirely orthogonal to that issue.
Last edited on
Topic archived. No new replies allowed.