Use const correctness to improve code safety and clarity
Const correctness in C++ refers to the practice of using the const keyword to ensure that variables, objects, and member functions do not unintentionally modify data, improving code safety and clarity.
Applying const is an expression of intent as much as it is an instruction to the compiler to verify that you conform to the expressed intent.
Omitting const is therefore just as much an expression of intent: This variable will be modified.
Be familiar with the C++ Core Guidelines about const correctness
Section titled “Be familiar with the C++ Core Guidelines about const correctness”- Con: Constants and immutability
- Con.1: By default, make objects immutable
- Con.2: By default, make member functions
const - Con.3: By default, pass pointers and references to
consts - Con.4: Use
constto define objects with values that do not change after construction - Con.5: Use
constexprfor values that can be computed at compile time
- F.49: Don’t return
const T
Additional Guidelines
Section titled “Additional Guidelines”Do not return const values
Section titled “Do not return const values”Returning a const value does not make sense, the client is able to copy the value into a non-const object anyway. It also disables move semantics, which is a constraint that has no benefits.
const Widget getWidget() { /* ... */ }// ...auto widget = getWidget() // Creates a mutable copywidget.change(); // Able to changeResources
Section titled “Resources”Prefer returning by value over returning by const reference for value objects
Section titled “Prefer returning by value over returning by const reference for value objects”When returning a const reference, you risk creating a dangling dangling reference:
- You might accidentally return a reference to a local object.
- You might return a reference to an object in an unstable container.
- Example: std::vector invalidates any existing references to elements when it resizes.
When returning a const reference, it also increases cognitive load on the reader, which can be avoided by using the Value Semantics . * F.15: Prefer simple and conventional ways of passing information
Using value semantics is more clear and appropriate for Value Objects, which represent data without identity.
- Return by-reference suggests shared/mutable state and that the specific object instance is important.
- Return by value, value semantics, suggest that the identity (specific instance) is not important.
Return by const reference can be used as optimization if it would be genuinely expensive to copy the object, but be aware of the risks.
class Widget { Point position_; std::string name_; std::vector<LargeData> cache_;
public: // ...
// AVOID - returning references to value objects const Point& get_position() const; const std::string& get_name() const;
// PREFER - return by value for value objects Point get_position() const; std::string get_name() const;
// OK - reference appropriate to avoid expensive copy const std::vector<LargeData>& get_cache() const;}Resources
Section titled “Resources”Arthur O’Dwyer’s Blog - Const all the things?
Section titled “Arthur O’Dwyer’s Blog - Const all the things?”- Function arguments:
- Pass cheap things by value:
int. - Pass expensive things by const reference:
const Widget&. - Pass out-parameters by pointer:
Widget*.
- Anything else: Wrong according to Arthur.
- Pass cheap things by value:
- Data members: never const
- Return types: never const
- Preserve invariants with
private, not withconst
- Preserve invariants with
Sandor Dargo’s Blog - When To Use Const
Section titled “Sandor Dargo’s Blog - When To Use Const”- Part I: functions and local variables
- Introduction to const. Recommends in particular to use const functions and const local variables.
- Part 2: member variables
- Be aware that const member variables are very restrictive
- Const member variables make an object not assignable
- That const member variables disable the move semantics of the object
- Be aware that const member variables are very restrictive
- Part 3: return types
- Return by const value can prevent optimizations (RVO), and it does not make sense to do so.
- Return by const reference risks dangling references, don’t create a habit out of it.
- Return by const pointer
- Be aware of const pointer vs pointer-to-const object
- Return pointer-to-const makes sense, the pointed to data cannot be modified
- Return const pointer to mutable object, does not make sense (in the same way as return by const value)
- Part 4: parameters
- Do not take small primitives by const reference, it is inefficient
- Take by const value is fine and recommended by Dargo.
- For objects take by const reference or possibly by value (copy). Never by const value.