DEV Community

Cover image for The Many Masks of `const`

The Many Masks of `const`

Basti Ortiz on January 27, 2019

Immutability is often considered to be a "best practice" in many programming languages. It reduces the likelihood of introducing unwanted side effe...
Collapse
 
dwd profile image
Dave Cridland

Impossible, eh? Strong words.

On a member function, the suffix const to a method declaration makes the implicit this argument itself const:

class Foo {
private:
  int m_field;
public:
  int field() const { return m_field; }
};

That Foo::field() is really:

int Foo_field(Foo * const this) {
  return this->m_field;
}

Since this is const, the field is also constant.

So far, that's what you said. But C++ is well known for giving the programmer ample opportunity to shoot themselves in the foot.

One option is the const-cast:

class Foo {
private:
  int m_field;
public:
  int field() const { return ++const_cast<Foo *>(this)->m_field; }
};

Which is demonstrably evil. The developer has blown away the const across the whole object. Like all casts, this isn't a safe thing to do, and needs careful validation - but there are niche cases where it might be the only solution to a problem.

A more controlled mechanism is to use mutable:

class Foo {
private:
  mutable int m_field;
public:
  int field() const { return ++m_field; }
};

The mutable keyword prevents the effect of a const this pointer, but only for a particular field. Now the field can be changed legally within any const method. Which is, on the face of it, a Bad Thing.

But there are a few legitimate cases for doing this. A good example is where you want to hold a lock during a read of a constant object. std::mutex::lock is a non-const method, so the std::lock_guard uses a non-const reference. All of which means this won't work:

class Foo {
private:
    int m_field = 0;
    std::mutex m_mutex;
public:
    int field() const {
        std::lock_guard<std::mutex> l__inst(m_mutex);
        return m_field;
    }
};

The solution is to make that mutex mutable - then the lock guard works and you're thread-safe.

But more importantly, while the method isn't "memory-const" anymore, it remains "semantically-const". To put it another way, unlike the previous examples I gave, this code behaves like you'd expect.

Collapse
 
somedood profile image
Basti Ortiz

Oh, boiii. That looks pretty messy if you'd ask me. I just love how C++ can give you an infinite number of ways to shoot yourself in the foot. This is really funny, yet concerning. 😬

I wouldn't really count the mutable keyword since that's an actual feature of the language that allows you to "bypass" the const declaration.

However, I would definitely count the crazy type-casting. That, right there, is why you have to love and hate C++ at the same time.

Thanks for sharing this! This is genuinely one of the more interesting comments I've seen in this site for a while.

Collapse
 
dwd profile image
Dave Cridland

Glad you found it interesting.

But the casts aren't inherently evil - they're a factor of C++ giving you all the tools you might need.

Collapse
 
codemouse92 profile image
Jason C. McDonald

I was just looking for a reference about this yesterday. Bravo!

Collapse
 
somedood profile image
Basti Ortiz

Dang, what are the odds of you coincidentally stumbling upon this article? That's amazing. 😁

Collapse
 
codemouse92 profile image
Jason C. McDonald

Not much of one. I was poking around Dev this morning, thought "Huh, I wonder what new stuff Some Dood has written," and there it is!

Thread Thread
 
somedood profile image
Basti Ortiz

It sounds so silly to say "I wonder what new stuff some dood has written" because of how vague and ambiguous it sounds when out of context. 😆 If someone outside the community read that, they would definitely ask what you meant by "some dood"? Ah, this is why I love this username. It just sounds so silly.

Thread Thread
 
codemouse92 profile image
Jason C. McDonald • Edited

Irrelevant, but one social IRC room I hang out in is inhabited entirely by Python programmers. We maintain an entire gallery of statements that come up during conversation, but sound ridiculous out-of-context.

Thread Thread
 
somedood profile image
Basti Ortiz

Yo, that sounds so funny. I'd love to read the humor of statements without context.

Anyway, we seem to be drifting quite far from your original comment. 😂 I wouldn't want to go too off-topic here. Thanks again for your nice comments! They really mean a lot.

Thread Thread
 
codemouse92 profile image
Jason C. McDonald

Follow me back, and we can take the conversation over to PM, yeah?

Thread Thread
 
somedood profile image
Basti Ortiz

GOOD IDEA! Why have I never thought about that before? I'll see you on the other side, my friend.

Collapse
 
phlash profile image
Phil Ashby

Thanks for the reminders!

Also of note: in many embedded systems const items stay in ROM, which provides runtime protection from bad people too ;)

Collapse
 
0xax profile image
0xAX

Reading variable declarations backwards will greatly help in remembering these nuances

This is the best advice which I've ever seen to remember these ambiguities in definition of const pointers

Collapse
 
somedood profile image
Basti Ortiz

Thanks, man! All the credit goes to the guy who wrote that amazing StackOverflow answer.