DEV Community

Discussion on: The Many Masks of `const`

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.