What Is a Constant?
Imagine you are writing a program to calculate the area and the circumference of a circle. The formulas are
Area = pi * Radius * Radius; Circumference = 2 * pi * Radius
In this formula, pi is the constant of value 22 / 7. You don’t want the value of pi to change anywhere in your program. You also want to avoid any accidental assignments of possibly incorrect values to pi. C++ enables you to define pi as a constant that cannot be changed after declaration. In other words, after it’s defined, the value of a constant cannot be altered. Assignments to a constant in C++ cause compilation errors.
Thus, constants are like variables in C++ except that these cannot be changed. Similar to variables, constants also occupy space in the memory and have a name to identify the address where the space is reserved. However, the content of this space cannot be overwritten. Constants in C++ can be
Literal constants
Declared constants using the const keyword
Constant expressions using the constexpr keyword (new since C++11)
Enumerated constants using the enum keyword
Defined constants that are not recommended and deprecated
Literal Constants
Literal constants can be of many types—integer, string, and so on. In your first C++ program in Listing 1.1, you displayed “Hello World” using the following statement:
std::cout << "Hello World" << std::endl;
In here, “Hello World” is a string literal constant. You literally have been using literal constants all the while! When you declare an integer someNumber, like this:
int someNumber = 10;
The integer variable someNumber is assigned an initial value of ten. Here decimal ten is a part of the code, gets compiled into the application, is unchangeable, and is a literal constant too. You may initialize the integer using a literal in octal notation, like this:
int someNumber = 012 // octal 12 evaluates to decimal 10
Starting in C++14, you may also use binary literals, like this:
int someNumber = 0b1010; // binary 1010 evaluates to decimal 10
Declaring Variables as Constants Using const
The most important type of constants in C++ from a practical and programmatic point of view are declared by using keyword const before the variable type. The generic declaration looks like the following:
const type-name constant-name = value;
Let’s see a simple application that displays the value of a constant called pi (see Listing 3.7).
LISTING 3.7 Declaring a Constant Called pi
1: #include <iostream> 2: 3: int main() 4: { 5: using namespace std; 6: 7: const double pi = 22.0 / 7; 8: cout << "The value of constant pi is: " << pi << endl; 9: 10: // Uncomment next line to view compile failure 11: // pi = 345; 12: 13: return 0; 14: }
Output
The value of constant pi is: 3.14286
Analysis
Note the declaration of constant pi in Line 7. We use the const keyword to tell the compiler that pi is a constant of type double. If you uncomment Line 11 where the programmer tries to assign a value to a variable you have defined as a constant, you see a compile failure that says something similar to, “You cannot assign to a variable that is const.” Thus, constants are a powerful way to ensure that certain data cannot be modified.
Constants are useful when declaring the length of static arrays, which are fixed at compile time. Listing 4.2 in Lesson 4, “Managing Arrays and Strings,” includes a sample that demonstrates the use of a const int to define the length of an array.
Constant Expressions Using constexpr
Keyword constexpr allows function-like declaration of constants:
constexpr double GetPi() {return 22.0 / 7;}
One constexpr can use another:
constexpr double TwicePi() {return 2 * GetPi();}
constexpr may look like a function, however, allows for optimization possibilities from the compiler’s and application’s point of view. So long as a compiler is capable of evaluating a constant expression to a constant, it can be used in statements and expressions at places where a constant is expected. In the preceding example, TwicePi() is a constexpr that uses a constant expression GetPi(). This will possibly trigger a compile-time optimization wherein every usage of TwicePi() is simply replaced by 6.28571 by the compiler, and not the code that would calculate 2 x 22 / 7 when executed.
Listing 3.8 demonstrates the usage of constexpr.
LISTING 3.8 Using constexpr to Calculate Pi
1: #include <iostream> 2: constexpr double GetPi() { return 22.0 / 7; } 3: constexpr double TwicePi() { return 2 * GetPi(); } 4: 5: int main() 6: { 7: using namespace std; 8: const double pi = 22.0 / 7; 9: 10: cout << "constant pi contains value " << pi << endl; 11: cout << "constexpr GetPi() returns value " << GetPi() << endl; 12: cout << "constexpr TwicePi() returns value " << TwicePi() << endl; 13: return 0; 14: }
Output
constant pi contains value 3.14286 constexpr GetPi() returns value 3.14286 constexpr TwicePi() returns value 6.28571
Analysis
The program demonstrates two methods of deriving the value of pi—one as a constant variable pi as declared in Line 8 and another as a constant expression GetPi() declared in Line 2. GetPi() and TwicePi() may look like functions, but they are not exactly. Functions are invoked at program execution time. But, these are constant expressions and the compiler had already substituted every usage of GetPi() by 3.14286 and every usage of TwicePi() by 6.28571. Compile-time resolution of TwicePi() increases the speed of program execution when compared to the same calculation being contained in a function.
Enumerations
There are situations where a particular variable should be allowed to accept only a certain set of values. These are situations where you don’t want the colors in the rainbow to contain Turquoise or the directions on a compass to contain Left. In both these cases, you need a type of variable whose values are restricted to a certain set defined by you. Enumerations are exactly the tool you need in this situation and are characterized by the keyword enum. Enumerations comprise a set of constants called enumerators.
In the following example, the enumeration RainbowColors contains individual colors such as Violet as enumerators:
enum RainbowColors { Violet = 0, Indigo, Blue, Green, Yellow, Orange, Red };
Here’s another enumeration for the cardinal directions:
enum CardinalDirections { North, South, East, West };
Enumerations are used as user-defined types. Variables of this type can be assigned a range of values restricted to the enumerators contained in the enumeration. So, if defining a variable that contains the colors of a rainbow, you declare the variable like this:
RainbowColors MyFavoriteColor = Blue; // Initial value
In the preceding line of code, you declared an enumerated constant MyFavoriteColor of type RainbowColors. This enumerated constant variable is restricted to contain any of the legal VIBGYOR colors and no other value.
Listing 3.9 demonstrates how enumerated constants are used to hold the four cardinal directions, with an initializing value supplied to the first one.
LISTING 3.9 Using Enumerated Values to Indicate Cardinal Wind Directions
1: #include <iostream> 2: using namespace std; 3: 4: enum CardinalDirections 5: { 6: North = 25, 7: South, 8: East, 9: West 10: }; 11: 12: int main() 13: { 14: cout << "Displaying directions and their symbolic values" << endl; 15: cout << "North: " << North << endl; 16: cout << "South: " << South << endl; 17: cout << "East: " << East << endl; 18: cout << "West: " << West << endl; 19: 20: CardinalDirections windDirection = South; 21: cout << "Variable windDirection = " << windDirection << endl; 22: 23: return 0; 24: }
Output
Displaying directions and their symbolic values North: 25 South: 26 East: 27 West: 28 Variable windDirection = 26
Analysis
Note how we have enumerated the four cardinal directions but have given the first North an initial value of 25 (see Line 6). This automatically ensures that the following constants are assigned values 26, 27, and 28 by the compiler as demonstrated in the output. In Line 20 you create a variable of type CardinalDirections that is assigned an initial value South. When displayed on the screen in Line 21, the compiler dispatches the integer value associated with South, which is 26.
Defining Constants Using #define
First and foremost, don’t use this if you are writing a program anew. The only reason this book analyzes the definition of constants using #define is to help you understand certain legacy programs that do define constants such as pi using this syntax:
#define pi 3.14286
#define is a preprocessor macro, and what is done here is that all mentions of pi henceforth are replaced by 3.14286 for the compiler to process. Note that this is a text replacement (read: non-intelligent replacement) done by the preprocessor. The compiler neither knows nor cares about the actual type of the constant in question.