5.2.3 · D5C++ Programming

Question bank — const correctness — const variables, const pointers, const member functions

2,139 words10 min readBack to topic

True or false — justify

Cover the answer. Give a verdict + one-sentence reason.

const int x = 5; folds into the binary as a true compile-time constant.
True when the initializer is itself a compile-time constant — the compiler can substitute 5 directly; a const initialized from a runtime value is just a read-only memory slot instead. (For a guaranteed compile-time value use constexpr — see constexpr and compile-time evaluation.)
const on a variable adds runtime overhead to enforce the promise.
False — enforcement happens entirely at compile time; the generated machine code is identical, const is a compile-time contract with zero runtime cost.
In const int* p, you cannot repoint p to another address.
False — only the pointee is protected, so p = &b; is fine but *p = 99; is the error.
A const object may have mutable members that still change.
True — mutable is the deliberate exemption; a const object's mutable int cacheHits can still be incremented inside a const method (see mutable and logical constness).
const-ness on an object is deep: everything reachable from it is frozen.
False — const is shallow/bitwise (see the figure above): a const object's pointer member becomes a const pointer, but the data it points to stays fully mutable.
You can call a non-const member function on a const object as long as it happens not to modify anything.
False — the compiler decides purely by the const keyword on the signature, not by what the body actually does; an "innocent" non-const method is still rejected on a const object.
int* const p must be initialized at its declaration.
True — it is a const object (the pointer itself), and like every const it has no later moment where a write is permitted, so it must be initialized now.
Two overloads char& at(int) and const char& at(int) const are an ambiguity error.
False — they differ in the const-ness of this, which is a valid overload distinction; the compiler picks based on whether the object is const. See const operator[].
Adding const to a read-only method is a purely cosmetic style choice.
False — without it, const objects and const& parameters simply cannot call the method at all, so it changes what code compiles.
const and volatile are the same kind of qualifier and cancel each other out.
False — volatile says "this memory may change outside the program's control, never optimize the reads away," while const says "this name won't write." They are independent, can appear together (const volatile int* p), and a const volatile value is read-only to you yet may still change under you.

Spot the error

State what won't compile (or is UB — undefined behavior) and why.

const int LIMIT;
LIMIT = 100;
``` ::: Two errors: a `const` must be initialized *at declaration* (`const int LIMIT;` alone is illegal), and even if it were, `LIMIT = 100;` is a forbidden write to a read-only variable.
 
```cpp
int a = 10;
const int* p = &a;
*p = 20;
``` ::: `*p = 20;` fails — `const` sits left of `*`, so it qualifies the pointee; writing *through* `p` is blocked even though `a` itself is not const.
 
```cpp
int a = 10, b = 20;
int* const p = &a;
p = &b;
``` ::: `p = &b;` fails — `const` sits right of `*`, so the *pointer* is frozen to `&a`; you may edit `*p` but never repoint `p`.
 
```cpp
class Circle { double r;
public: double area() const { r = r * 2; return 3.14*r*r; } };
``` ::: Inside a const method `this` is `const Circle*`, so `r` is read-only — the assignment `r = r * 2;` is rejected.
 
```cpp
const Circle c{};
c.setR(5.0);
``` ::: `c` is const so its `this` is `const Circle*`; `setR` is non-const and cannot accept that `this`, so the call is rejected.
 
```cpp
const int k = 42;
int* q = const_cast<int*>(&k);
*q = 0;
``` ::: It compiles, but `k` was *originally declared* `const`, so writing through the `const_cast` pointer is **undefined behavior (UB)** — see [[Undefined Behavior in C++]]; `const_cast` is only safe when the underlying object was truly non-const.
 
```cpp
void print(const char* s);
char msg[] = "hi";
char* const locked = msg;
print(locked);
``` ::: This one is fine — `char* const` (const pointer) converts freely to `const char*` (pointer to const) for the parameter; the trap is thinking the two `const`s conflict. They apply to different things.
 
---
 
## Why questions
 
Answer with the *mechanism*, not a restatement.
 
Why must a const variable be initialized at its declaration? ::: Because no later assignment is ever legal, the declaration is the only moment a value can be written — miss it and the variable is permanently unwritable.
Why is "pointer to const" (`const int*`) the workhorse for function parameters? ::: It lets the function promise "I only read your data," and it also lets *const callers* pass their data in — a non-const-pointer parameter would reject const arguments. See [[Function parameterspass by value vs const reference]].
Why do we read pointer declarations right-to-left? ::: Because `const` binds to whatever is on its immediate left, so scanning right-to-left lets you attach each qualifier to the correct target in the order it applies.
Why does a `const` method change the type of `this`? ::: Marking the method `const` turns `this` from `Circle*` into `const Circle*`, which is the actual mechanism that makes every data member read-only inside the body.
Why should you mark read-only methods `const` up front rather than later? ::: Retrofitting forces a cascade of edits across every const path, and until then any `const&` parameter is unable to call the method — so the omission blocks callers you haven't written yet. See [[Classes and the this pointer]].
Why does `mutable` exist if `const` is supposed to mean "unchanging"? ::: It separates *logical* constness (the observable state) from *bitwise* constness; caches, mutexes and hit counters can change without altering what the object logically represents (see [[mutable and logical constness]]).
Why is `const int MAX = 100;` preferred over `#define MAX 100`? ::: The `const` version is typed and scoped and visible to the debugger, whereas `#define` is blind text substitution with no type and no scope.
Why is `const_cast`-then-write on a truly-const object undefined behavior rather than just a warning? ::: Because the compiler may have placed a truly-const object in read-only memory or folded its value into the code; the standard refuses to define what a write does so it can keep those optimizations — hence UB (see [[Undefined Behavior in C++]]).
 
---
 
## Edge cases
 
Cover the boundary and degenerate scenarios the topic quietly assumes.
 
What happens to a `const` object that has *no* const member functions at all? ::: It becomes nearly useless — you can construct and destroy it but cannot call any of its methods, which is exactly why every read-only method should be `const`.
Does `const int* const p` require initialization? ::: Yes — the pointer part is const, and a const pointer (like any const) must be initialized at declaration; neither `p` nor `*p` can change afterward.
If a const object holds a pointer member `int* data;`, is `data` itself const or `*data`? ::: `data` becomes a const pointer (you can't repoint it), but `*data` stays fully mutable — a direct consequence of const being **shallow**, not deep (see the deep-vs-shallow figure above).
Can a const method and a non-const method with identical parameters coexist? ::: Yes — const-ness of `this` is part of the signature, so `at(int) const` and `at(int)` overload cleanly and the compiler resolves by the object's const-ness.
Is `const_cast`-ing away const *ever* well-defined? ::: Only when the object you're pointing at was *not originally declared* const; casting away const then modifying is legal, but doing it to a truly-const object is undefined behavior.
Can you have a `const` reference to a non-const object? ::: Yes — `int a = 5; const int& r = a;` is fine; `r` promises *it* won't modify `a`, but other names for `a` may still change it, so reading `r` can see different values over time.
What does calling a `const` method on a *non-const* object do? ::: It works perfectly — a non-const object can call both const and non-const methods; const on the method only restricts what the method does, never who may call it.
What does a **const rvalue reference** like `const std::string&& r` even mean, and why is it nearly useless? ::: It binds to a temporary but *promises not to modify it* — which defeats the whole point of an rvalue reference (moving *steals* from the temporary, which requires writing to it), so `const&&` cannot move from and is a rare, almost always mistaken, signature.
Can `const` and `volatile` qualify the same object at once? ::: Yes — `const volatile int status;` is legal and means "*I* may only read it, and the compiler must re-read it every time because hardware may change it"; it is the classic type for a read-only memory-mapped hardware register.
 
---
 
> [!mnemonic] The one line to remember
> **Const binds left; const object → const `this`; const is shallow; casting away real const is UB (undefined behavior).** Four traps, one breath.