04 / 163 · C++11 · 8 min
const: Read-Only Access Paths and Constant Objects
const restricts modification through a given type, but a read-only reference does not mean the underlying object never changes. Understanding top-level versus low-level const, the read-only promise of member functions, and the difference between const and constexpr is what lets you write interfaces that are reliable without over-promising.
In this lesson
Ask first who cannot modify
const int* p is a pointer to a read-only int: you may change where p points, but you may not write the integer through p. int* const p fixes the pointer itself but allows modification of the non-const integer it points to. const int* const p applies both restrictions; you must distinguish these two levels when reading a declaration.
A read-only access path does not guarantee that the object is unchanged elsewhere. An ordinary int can be referred to by both int& and const int&; after the former modifies it, the latter observes the new value. Conversely, if the object was defined as const, actually modifying it by bypassing the type restriction is undefined behavior, not a reliable “forced modification.”
const answers “what may this path do,” not “what may the whole program do.” Top-level const on a pointer freezes the pointer value; low-level const freezes the int reached through it. A const object and a const view of a mutable object are different situations and have different rules.
The const promise in an interface
For a large read-only input, const T& avoids a copy and constrains the function from modifying the object through that reference; small integers are usually passed by value. Top-level const on a value parameter does not participate in distinguishing function types, so you cannot create two overloads with f(int) and f(const int).
A const member function restricts modification of ordinary members through this and makes the function usable on const objects. It is not a deep immutability guarantee: an object pointed to by a pointer member may still be modified, and mutable members can change. An interface should express logical read-only access, not treat const as an automatic thread-safety marker.
Put const on the access path you actually want to restrict. Callers then see whether the function may mutate through that parameter or through this. Do not expect const on a pointer member, or const on a member function, to freeze every object reachable from the function.
const is not compile-time evaluation
A const variable may be initialized at run time and, after initialization, cannot be modified through ordinary access; a constexpr variable requires constant-expression initialization and similar rules, and implies const. Some const integers initialized with constant expressions can also be used in compile-time contexts, but that does not make every const object a compile-time constant.
The example lets a read-only reference observe a legitimate change to an ordinary object, then shows that a const pointer can modify its target. In real development, keep const from the start of the interface and avoid repairing a wrong signature with const_cast deep in the code; such a conversion makes true modification rights harder to audit.
Use const for access control after initialization. Use constexpr when you need a constant-expression object. The two overlap for some integers, but they are not the same tool, and a later const_cast is a sign that the original signatures did not record who was allowed to write.
Pitfalls
- A const pointer member only fixes the pointer value; it does not recursively freeze the object the pointer points to.
- auto x = const_object usually deduces a non-const value copy; to keep a reference and read-only access, write const auto& explicitly.
Run an example
Minimum C++11 · complete program · Download .cpp
#include <cassert>
#include <iostream>
int main() {
int value = 3;
const int& read_only = value;
value = 7;
assert(read_only == 7);
int* const fixed_pointer = &value;
*fixed_pointer = 9;
const int* read_pointer = &value;
assert(*read_pointer == 9);
constexpr int limit = 10;
static_assert(limit > 0, "positive limit");
std::cout << read_only << ' ' << limit << '\n';
}
Compile locally
g++ -std=c++11 -Wall -Wextra -Wpedantic -pthread basics-const.cpp -o example && ./exampleExpected result
9 10
CHECK YOUR UNDERSTANDING
Close the answer. Explain it.
In using P = int*; const P p = &n;, is p a pointer to const int? Can you write *p = 5?
Show a reference answer
No. P already names the complete pointer type; const P is equivalent to int* const, so const qualifies the pointer itself. As long as n is a live non-const int, *p = 5 is legal; p cannot be reseated to another object. To point to a read-only int, write const int*, or introduce a separate alias for that type.
Check the sources
Drafts and official chapters change. The version mark is only the example’s minimum.