I was looking through some code examples last week and realized there was a gap in my understanding of the const keyword in C++. I have always written definitions like “const MyClass& c”, but in the code I was reading I was seeing a lot of “MyClass const & c”. Now intuitively I was thinking that this must be basically the same thing, but since I wasn’t entirely positive I decided to do a little research. It turns out that the use of the const keyword is a bit more complicated than I originally thought.
In the two examples I mentioned above, the declarations are functionally equivalent. No difference, just a matter of preference. For myself I find that putting the const keyword first is the most readable. But I understand that some developers prefer the second. I have read that people who prefer to read their declarations right-to-left particularly prefer this, as it reads like “c is a reference to a constant MyClass”, or something like that. I’m not really able to identify one as better than the other, so I will continue to use my own preference in my code, and if I am working with someone elses code who prefers the second form, I can conform to that as well.
But as I delved a bit further into this I started looking at pointer declarations. I have seen const dropped in seemingly random places in a declaration: “<const> int <const> * <const> x”. It turns out that the placement of the const keyword does make a different declaration in some of these cases. The const keyword modifies the item just to its left, unless it is the left-most thing in the expression, when it modifies the item to its right. So “const int * x” is identical to “int const * x”, but “int * const x” is a WHOLE different beast. The first two forms make the int a constant value, while the third makes the POINTER itself a constant value.
To illustrate, here is a sample function that declares the same kind of pointer (to an integer) but with varying styles of using const. In each case, the pointer is used to try to modify the pointed-to integer, and then the pointer itself is updated to point to a different underlying integer. The compiler fails some of these operations depending on the const keywords usage, each of the failures is noted with a comment.
int x = 1; int x2 = 2; // Non-const pointer to an integer int* p1 = &x; *p1 = 42; p1 = &x2; // Non-const Pointer to a const integer (two styles) const int* p2a = &x; *p2a = 42; // error C3892: 'p2a' : you cannot assign to a variable that is const p2a = &x2; int const * p2b = &x; *p2b = 42; // error C3892: 'p2b' : you cannot assign to a variable that is const p2b = &x2; // Const pointer to a non-const integer int * const p3 = &x; *p3 = 42; p3 = &x2; // error C3892: 'p3' : you cannot assign to a variable that is const // Const pointer to a const integer const int * const p4 = &x; *p4 = 42; // error C3892: 'p4' : you cannot assign to a variable that is const p4 = &x2; // error C3892: 'p4' : you cannot assign to a variable that is const