const in C++
Every C++ programmer must’ve at one point struggled with the subtle differences between the following declarations:
1 2 3 4 5 6 7 const T num; T const num; T *ptr; T const *ptr; T *const ptr; T const *const ptr; const T *const ptr;
In the example above,
T is just a type placeholder that can be replaced with
std::string, an STL container, or any custom class you like.
To understand the declarations in the example, we need only understand the one rule of C++’s
const qualifier: “
const applies to what’s immediately on the left. If there’s nothing on the left, apply to its immediate right.”
Let’s break this down further.
constqualifier is applying to some
T(a type, like
std::unordered_map<int, int>), it is making the
Tconstant. This means the
Tis immutable, despite the pointer pointing to it (if there is one) being potentially mutable.
constqualifier is applying to
*, it’s making the pointer itself constant. This means the pointer cannot change the address it points to, yet the value at that address is potentially mutable.
With this in mind, it shouldn’t be hard to decode the declarations above:
1 2 3 4 5 6 7 const T obj; // obj is a constant T T const obj; // obj is a constant T T *ptr; // ptr is a mutable pointer to a mutable T T const *ptr; // ptr is a mutable pointer to a constant T T *const ptr; // ptr is a constant pointer to a mutable T T const *const ptr; // ptr is a constant pointer to a constant T const T *const ptr; // ptr is a constant pointer to a constant T
What about references (
For references, there is less ambiguity, because we cannot apply the
const qualifier to the “reference” itself anyways:
1 2 3 const T &obj = 0; // obj is a reference to a constant T T const &obj = 0; // obj is a reference to a constant T T &const obj = 0; // ERROR: Invalid Reference Qualifier Application
East const or West const?
Like tabs vs. spaces and the editor wars (Vi vs. Emacs), East const vs. West const has been a long rivalry in the programmer community (at least for C++ developers), with West const being the conventional standard and East const being the modern revolution bringing arguments such as “more consistent and logical”. To demonstrate the differences, consider the following example:
1 2 3 4 5 const int num = 0; // West const int const num = 0; // East const const T *const ptr; // West const T const *const ptr; // East const
Personally, I prefer to use West const because I think it reads more intuitive (
const int declares a “constant integer”), is more popular in existing code bases, and is the recommended style in C++ Core Guidelines. Additionally,
const T &is the style used in Bjarne Stroustrup’s The C++ Programming Language book.
const T &is the style used in the C++ standard itself.
const T *is the style used in K&R’s The C Programming Language book.
const T *is the style used in the C standard.
However, East const does have the advantage of being super consistent, always applying
const to its immediate left with no exceptions (I.e. there will always be something on the left to apply to, because
const int num would be written as
int const num in East const style, for instance.). To read about more arguments for East const, consider this well-written blog post by an East const advocate.
As in all programming style-related debates, just pick one and be consistent with it throughout a project. It also helps to understand different styles so we never get confused regardless of the code base.
That’s all I have this time, thanks for stopping by.