1

This is related to the (currently) closed question I asked earlier: Can you mutate an object of custom type when it's declared as constant?

Suppose we have something that looks like the following:

class test 
{
public:
  test() : i{4}, ptr{&i} {};
  int i;
  int *ptr;
  int *get_ptr() const {return ptr;}
};

void func(const test &t, int j) {
  *(t.get_ptr()) = j;
  // auto ptr = t.get_ptr();
  // *ptr = j;
}


int main(int argc, char const *argv[])
{
  test t;
  std::cout << t.i << std::endl;
  func(t, 5);
  std::cout << t.i << std::endl;
}

We have this func that takes in a const test &. When I see this signature (and if I didn't look at the implementation of the function), it makes me want to assume that nothing in t will get modified; however, the member variable i is able to be modified through the ptr member variable, as we see here.

I don't usually write code that end up working this way, so I'm wondering if code like this is discouraged?

Furthermore, is it reasonable to assume (most of the time) that an object declared as const will not be mutated?

2
  • 3
    This is a great example of C++ allowing you to shoot yourself in the foot. int *ptr can point to an internal int during construction, because const objects only become const after construction. Footgun locked and loaded. Commented Jan 25, 2022 at 21:09
  • 1
    You're cheating: You don't directly modify the passed object (for example, try changing what the ptr member points to). The fact that it points at another member variable is something the compiler won't check. Commented Jan 25, 2022 at 21:11

2 Answers 2

4

Yes code like this is definitely discouraged. It completely ignores the reason we have the const keyword in the first place. The function is deliberately modifying something that it is advertising it will not modify

That means that whoever wrote the get_ptr() function messed up because they declared it const but let it return a pointer to non-const object (so one that can be changed, defeating the purpose of declaring the function const)

If whatever get_ptr() returns could properly be modified by such a function then it should be an implementation detail, a private (or protected) variable marked with the mutable keyword.

test::get_ptr() should have two overloads.

const int* get_ptr() const { return ptr; }
int*       get_ptr()       { return ptr; }

If func() wants to change the test object given to it then it should take test&, not const test&

1
  • Oops you're right. My eyes glossed over. Thought they were changing the pointer, not the value pointed to. Fixed
    – Joe
    Commented Jan 25, 2022 at 21:18
3

The key is here:

  int *get_ptr() const {return ptr;}

You define get_ptr as const which is akin to saying "get_ptr is not going to change any attributes of the class". And it doesn't. It returns the value of the attribute ptr, which is a int*, and points to a mutable instance of an int which happens to be an attribute of the class as well.

The compiler has no way of knowing this, so from the compiler's perspective the promise was kept. However in reality you're circumventing the const qualifier and allowing to mutate an otherwise immutable attribute.

Not the best of coding practices, but whoever writes such code should, obviously, not expect for i to remain as it was set in the class methods if get_ptr is ever called.

Not the answer you're looking for? Browse other questions tagged or ask your own question.