const correctness — const refs, const methods & when to use

RoadmapsC++

Scenario

You pass a large std::string to a function by reference to avoid copying it. Another developer calls your function with a string literal, but the compiler throws an error saying 'cannot bind non-const lvalue reference'.

How do you promise the compiler that you're just looking at the data, not changing it?

Mental model

const is a legally binding contract with the compiler. It means 'I promise not to mutate this.' If you try to break the promise, the code refuses to compile.

const correctness is the practice of using the `const` keyword proactively to prevent unintended state changes. Passing a large object by `const std::string&` avoids a copy AND promises not to change it. A `const` method (e.g., `int getSize() const`) promises not to modify any member variables of the class. This creates a chain of trust: const objects can only call const methods.

Explanation

When you pass a large object to a function by value, C++ copies it. To avoid the copy, you pass it by reference. However, a plain reference (`&`) allows the function to modify the object, and crucially, you cannot bind a plain reference to a temporary object (like a string literal or a function return value).

The fix is `const Type&`. It gives you the performance of a reference with the safety of a copy. It also allows binding to temporary values.

Inside a class, putting `const` at the end of a method signature (e.g., `void print() const;`) marks the method itself as constant. This tells the compiler that calling this method won't change the object's state. If you have a `const` object, you can ONLY call `const` methods on it. This is why getters should always be marked `const`.

When combining `const` with pointers, read the declaration from right to left to understand what is constant. For example, `const int* ptr` (or `int const* ptr`) means 'pointer to an integer that is constant'—you can change where the pointer points, but you cannot modify the integer it points to. Conversely, `int* const ptr` means 'constant pointer to an integer'—you can modify the integer, but you cannot change where the pointer points. `const int* const ptr` combines both: a constant pointer to a constant integer.

Code examples

The bug: Missing const in parameters and methods

#include <string>
#include <iostream>

class User {
    std::string name;
public:
    User(std::string n) : name(n) {}
    // Missing const!
    std::string getName() { return name; }
};

// Takes a plain reference
void printName(User& u) {
    std::cout << u.getName() << '\n';
}

int main() {
    // This fails to compile! A temporary User cannot bind to a non-const reference.
    printName(User("Alice")); 
    return 0;
}

`User("Alice")` is a temporary object (an rvalue). Plain lvalue references (`User&`) cannot bind to temporaries because modifying a temporary makes no sense. The code is broken.

The fix: const correctness

#include <string>
#include <iostream>

class User {
    std::string name;
public:
    User(std::string n) : name(n) {}
    // Marked const: promises not to modify members
    std::string getName() const { return name; }
};

// Takes a const reference: binds to temporaries and avoids copying
void printName(const User& u) {
    std::cout << u.getName() << '\n'; // Allowed because getName() is const
}

int main() {
    printName(User("Alice")); // Works perfectly!
    return 0;
}

By changing the parameter to `const User&`, we can pass temporaries. However, inside `printName`, `u` is `const`. The compiler will only let us call `u.getName()` if `getName()` is explicitly marked `const`.

Key points

Common mistakes

Recall questions

Questions & answers

A C++ program has a `const std::vector<int>& data` parameter. Inside the function, the programmer tries to sort the vector. What happens and why?

The code will not compile. The `const` keyword acts as a contract that the object cannot be mutated. `std::sort` requires modifying the container's elements, which violates the `const` qualifier.

Approach: Understand that const is enforced at compile time, rejecting any mutating operations.

In an interview, you see `const int* const ptr`. What does this mean?

It means a constant pointer to a constant integer. The integer cannot be modified through the pointer, and the pointer itself cannot be reassigned to point to another integer.

Approach: Read declarations from right to left: 'const pointer to const int'.

Continue learning

Previous: References vs pointers — syntax & mental model

Next: Functions — pass by value, reference & pointer

Return to C++ Roadmap