A constant is a variable that has a value which cannot be changed once the constant has been assigned. Unlike variables, constants never change in value. You must initialize a constant when it is created. This allows the compiler to enforce that the variable’s value is not changed anywhere in the code by mistake.
A variable can be made into a constant by adding the const
keyword either before or after the data type. The variable becomes read-only once the const
modifier added and the value must be assigned at the same time as it is declared. Attempting to change the value anywhere in the program will results in a compile-time error.
const int var = 5; int const var2 = 10; // alternative order
In pointers, const
can be used in two ways.
First, the pointer can be made constant, which means that it cannot be changed to point to another location.
int myPointee; int* const p = &myPointee; // pointer constant
Second, the pointee can be declared constant. This means that the variable pointed to cannot be modified through this pointer.
const int* q = &var; // pointee constant
It is possible to declare both the pointer and the pointee as constant to make them both read-only.
const int* const r = &var; // pointer & pointee constant
Note that constant variables may not be pointed to by a non-constant pointer. This prevents programmers from accidentally rewriting a constant variable using a pointer.
int* s = &var; // error: const to non-const assignment
References can be declared constant in the same way as pointers. However, since reseating a reference is never allowed, declaring the reference as const would be redundant. It only makes sense to protect the referee from change.
const int& y = var; // referee constant
Just as with variables, pointers and references, objects can also be declared constant. Take the following class as an example.
class MyClass { public: int x; void setX(int a) { x = a; } };
A constant object of this class cannot be reassigned to another instance. The constness of an object also affects its fields and prevent them from being changed.
const MyClass a, b; a = b; // error: object is const a.x = 10; // error: object field is const
Because of this last restriction, a constant object may not call a non-constant method since such methods are allowed to change the object’s fields.
a.setX(2); // error: cannot call non-const method
They may only call constant methods, which are methods that are marked with the const modifier before the method body.
int getX() const { return x; } // constant method
This const modifier means that the method is not allowed to modify the state of the object and can therefore safely be called by a constant object of the class. More specifically, the const modifier applies to the this pointer that is implicitly passed to the method. This effectively restricts the method from modifying the object’s fields or calling any non-constant methods in the class.
In addition to making a method constant, the return type and method parameters may also be made read-only. For example, if a field is returned by reference instead of by value from a constant method it is important that it is returned as a constant in order to maintain the constness of the object. Not all C++ compilers will be able to catch this subtle mistake.
const int& getX() const { return x; }
Both static and instance fields in a class can be declared constant. A constant instance field must be assigned its value using the constructor initialization list. This is the same as the preferred way of initializing regular (non-constant, non-static) fields.
class MyClass { public: int i; const int c; MyClass(): c(5), i(5) {} }
A constant static field has to be defined outside of the class declaration, in the same way as non-constant static fields. The exception to this is when the constant static field is of an integer data type. Such a field may also be initialized within the class at the same time as the field is declared.
class MyClass { public: static int si; const static double csd; const static int csi = 5; }; int MyClass::si = 1.23; const double MyClass::csd = 1.23;
The keyword constexpr was introduced in C++11 to indicate a constant expression. Like const it can be applied to variables to make them constant, causing a compilation error if any code attempts to modify the value. constexpr int myConst = 5;
myConst = 3; // error: variable is const
Functions and class constructors may also be defined as constant expressions, which is not allowed with const. Using constexpr on a function limits what the function is allowed to do. In short, the function must consist of a single return statement, and it can only reference other constexpr functions and global constexpr variables. C++14 relaxes these constraints, allowing constexpr functions to contain other executable statements.
constexpr int getDefaultSize(int multiplier) { return 3 * multiplier; }
The return value for a constexpr function is guaranteed to be evaluated at compile time only when its arguments are constant expressions and the return value is used where a compile-time constant is necessary.
// Compile-time evaluation int myArray[getDefaultSize(10)]; If the function is called without constant arguments, it returns a value at runtime just like a regular function. // Run-time call int mul = 10; int size = getDefaultSize(mul);
Constructors can be declared with constexpr, to construct a constant expression object. Such a constructor must be trivial.
class Circle { public: int r; constexpr Circle(int x) : r(x) {} };
When called with a constant expression argument, the result will be a compile-time generated object with read-only fields. With other arguments it will behave as an ordinary constructor.
// Compile-time object constexpr Circle c1(5); // Run-time object int x = 5; Circle c2(x);
In general, it is a good idea to always declare variables as constants if they do not need to be modified. This ensures that the variables are not changed anywhere in the program by mistake, which in turn will help to prevent bugs. There is also a performance gain by allowing the compiler the opportunity to hard-code constant expressions into the compiled program. This allows the expression to be evaluated only once – during compilation – rather than every time the program runs.