The Importance of const Correctness

Learn more about the use of const and why we should use it throughout our application.
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

3D art showing a fantasy maid character
Ryan McCombe
Ryan McCombe
Posted

Following on from our lesson on the many uses of const, lets discuss when we should, and when we shouldn't, make use of it.

In the below example, as y was not marked as const.

int Add(const int& x, int& y) {
  return x + y;
}

int x = 1, y = 2;
Add(x, y);

But, it is easy to tell from this simple example that y will never be changed. We just didn't add the const directive to make that clear.

Our code is valid, but not "const correct".

In general, we should always try to add the const (or constexpr) keyword where it is useful. The usefulness of const depends greatly on context.

The above example - passing a reference or a pointer to a function - is the scenario where being const correct is most important.

This is because when someone passes a variable they created to a function, they are likely going to be very interested in whether or not our function is going to modify their variable. That could have implications on how they write the rest of their function, because they may need to create additional code to deal with that possibility their variable has changed.

If we write a function that accepts a pointer or reference argument, and we do not mark that argument as const, anyone who calls our function will have a reasonable expectation that their variable could be modified.

If that is not true, and in reality we just neglected to specify the parameter as const, our omission could indirectly create extra work and performance overhead.

When to avoid using const

Even though there are many situations where we can use const, it's not always a good idea.

Firstly, there's almost no reason to use const for a value returned by a function:

const int Add(int x, int y) {
  return x + y;
}

Due to the way values are returned from functions, the use of const in that context does not behave like we might expect. It can also degrade performance. We cover this in more detail later.

Also, const is less important for arguments that are passed by value. Passing by value means the incoming data is copied. Callers of a function are generally not going to care about what happens to a copy of their data.

As such, it is fairly common to not mark these argument types as const, even if they're not being modified.

Here, both our parameters could be const, but is it really worth making our function signature more complicated?

int Add(int x, int y) {
  return x + y;
}

Similarly, it may not be worth marking a simple local variable as const. In the following example, Damage could be const, but the variable is discarded after two lines of code, so is it really worth it?

void TakeDamage() {
  int Damage { isImmune ? 0 : 100 };
  Health -= Damage;
}

Different people and companies will have different opinions on these topics.

The Unreal coding standard asks for const to be used anywhere it is applicable:

Const is documentation as much as it is a compiler directive, so all code should strive to be const-correct. ... Const should also be preferred on by-value function parameters and locals. ... Never use const on a return type.

Google's style guide is less prescriptive, suggesting we "use const whenever it makes sense":

  • they encourage const to be used for function parameters that are pass-by-reference
  • they encourage const to be used for class methods that do not modify the object
  • they are indifferent to the use of const for local variables
  • they discourage const for function parameters that are passed by value

Removing const with const_cast

Whilst we should try to make our code const correct, we're inevitely going to find ourselves interacting with other code that isn't.

When this happens, we'd ideally want to update that other code, but that isn't always an option. For example, that code might be in a 3rd party library that we're not able to modify.

This can leave us in a situation like this:

// Cannot modify this
namespace SomeLibrary {
  int Double(int& x) {
	return x * 2;
  }
}

int MyFunction(const int& x) {
  return SomeLibrary::Double(x);
}

We have a function receiving a constant reference. We want to pass that reference into a function that will not modify it, but that function has not formalised that by marking the parameter const.

As such, our call to the library function will throw an error.

For these situations, we have const_cast. This will remove the "constness" of a reference, or of the value pointed to by a pointer.

int x { 5 }
const int* Pointer { &x };
const int& Reference { x };

int* NonConstPointer { const_cast<int*>(Pointer) };
int& NonConstReference { const_cast<int&>(Reference) };

Following the above code, any function expecting a pointer or reference to a non-const version of x can be given NonConstPointer or NonConstReference respectively.

Updating our previous example to use const_cast would look something like this:

// Cannot modify this
namespace SomeLibrary {
  int Double(int& x) {
	return x * 2;
  }
}

int MyFunction(const int& x) {
  return SomeLibrary::Double(
    const_cast<int&>(x)
  );
}

Adding const using const_cast

const_cast can also be used to add constness to a reference or pointer:

int x { 5 };
int& Reference { x };

const int& ConstantReference {
  const_cast<const int&>(Reference)
};

However, this is generally not useful. We've already seen how a non-const reference can be implicitely converted to a const reference.

Relying on this implicit cast is generally preferred in this scenario, compared to the more verbose explicit casting.

Was this lesson useful?

Ryan McCombe
Ryan McCombe
Posted
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Clean Code
3D art showing a progammer setting up a development environment
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Free, unlimited access!

This course includes:

  • 66 Lessons
  • Over 200 Quiz Questions
  • Capstone Project
  • Regularly Updated
  • Help and FAQ
Next Lesson

Effective Comments and JavaDoc

Learn what makes for useful and effective comments, and some scenarios where we shouldn't use comments at all
3D art showing a maid character
Contact|Privacy Policy|Terms of Use
Copyright © 2023 - All Rights Reserved