Class field modifiable inside a const method if field passed by reference?

Viewed 123

C++ student here:

Today, while writing a class, I noticed that I am able to modify a non-mutable field with a const method, if the field is passed by reference:

class Foo {

public:
    void func1(int & _n) const { _n = 42; }
    void func2() { func1(n); }

private:
    int n;
};

int main() {
    Foo foo;
    foo.func2();

    return EXIT_SUCCESS;
}

I know that it's possible because a const method simply uses a this to const, but if I pass the field as a reference, I can have direct access to it without passing through the this (I'm still learning and also still not very good at English, I may have said something wrong);

So, my question is:

What's the true utility of a const method, if a non-mutable field can be modified with a simple "trick"?

Thanks!

4 Answers

What the true utility of a const method

It allows you to have the compiler maintain class invariants. But the compiler isn't going to protect your from shooting yourself in the foot. Inside a const member function, the compiler will only prevent modification of class members by name.

If you pass a reference to the data from outside, then it's on you. There will not be any convoluted code emitted that verifies you didn't pass a reference to a member.

The same goes for access modifiers. They only prevent access to the name of the member. You can break that encapsulation in many a way, the compiler isn't going to bother stopping you.

The language has mechanisms that protect you from Murphy, not Machiavelli. They won't help if you use "tricks" and work against the grain of the language. Proper encapsulation is up to the programmer, not the compiler.

In the real world you wouldn't give out public access to an internal class member. When you do this, all bets are off.

Const methods help you enforce encapsulation of a class, but you need to have this encapsulation in the first place - by marking the private data private.

Just define const Foo foo; instead of Foo foo; and compiler will catch the error and will be your friend again.

that I am able to modify a non-mutable field

You are not able to modify a non-mutable field, because the field isn't non-mutable. Try to declare n as const and you will see that the code will no longer compile, and that no trick (short of abusing const_cast and invoking undefined behaviour) will make it compile anymore.

The only const in your code is the one for func1, which turns the "invisible" Foo* const this (non-modifiable pointer to modifiable Foo) in the function into Foo const* const this (non-modifiable pointer to non-modifiable Foo).

As it turns out, those two imaginary consts work perfectly as designed, because both of the following attempts will fail:

  • this = nullptr; // won't compile
  • this->n = 42; // won't compile

However, none of those two consts are related to the _n argument. _n and this->n refer to the same int object, but only the latter of the two sees it as const.

This leads to an important discovery. Having a const reference or pointer to something and having a reference or pointer to an immutable type are two different things. int is mutable, so whenever you have a non-const reference or pointer to one, modifications are possible, regardless of whether other parts of the program view the int object as const.

What's the true utility of a const method, if a non-mutable field can be modified with a simple "trick"?

As I said above, the field itself is not non-mutable. And if you need a "trick" to shut up the compiler, then you are just pushing your luck anyway.

Related