C++ course menu
C++ Constants: const, constexpr, Literals and Avoiding Magic Numbers
Learn to make values that cannot change in C++: const variables, constexpr compile-time constants, constexpr functions, literal suffixes and digit separators, and why named constants beat magic numbers and #define.
On this page
Some values should never change while a program runs: the number of days in a week, a tax rate, the size of a game board. Marking them as constants documents that intent, and the compiler stops anyone (including you) from changing them by accident.
const: a value that cannot change#
Put const before the type. A constant must be initialized when it is created:
#include <iostream>
int main() {
const double gst_rate = 0.18;
const int days_per_week = 7;
double price = 1000;
std::cout << "Tax: " << price * gst_rate << '\n';
std::cout << "Hours in a week: " << days_per_week * 24 << '\n';
}Tax: 180
Hours in a week: 168Trying to change a constant is a compile error:
int main() {
const int max_users = 100;
max_users = 200; // error: assignment of read-only variable
}A const value can come from a calculation done while the program runs, such as user input. It just cannot change afterwards.
constexpr: known at compile time#
constexpr goes one step further: the value must be computable while compiling. That lets the compiler use it anywhere a fixed number is required, such as the size of an array, and it costs nothing at run time:
#include <array>
#include <iostream>
int main() {
constexpr int board_size = 8;
constexpr int squares = board_size * board_size;
std::array<int, squares> board{};
std::cout << "Squares: " << board.size() << '\n';
}Squares: 64A rule of thumb: use constexpr for fixed values written in your code, and const for values that are calculated at run time but should not change afterwards.
constexpr functions#
A function marked constexpr can be evaluated by the compiler when its arguments are constants, and still be called normally with run-time values:
#include <iostream>
constexpr int square(int n) {
return n * n;
}
int main() {
constexpr int area = square(12); // calculated while compiling
int side = 5;
std::cout << area << ' ' << square(side) << '\n';
}144 25Avoid magic numbers#
A magic number is an unexplained value in the middle of code. Compare:
// What is 0.18? What is 3?
double total = price * 1.18;
if (attempts > 3) { /* ... */ }constexpr double gst_rate = 0.18;
constexpr int max_login_attempts = 3;
double total = price * (1 + gst_rate);
if (attempts > max_login_attempts) { /* ... */ }The second version explains itself, and if the rate changes you update one line.
Why not #define?#
Older code defines constants with the preprocessor:
#define MAX_USERS 100#define is plain text replacement done before compiling: it has no type and ignores scope, so the name can clash with anything. Prefer constexpr int max_users = 100;, which has a type, follows normal scope rules and shows up in the debugger.
Literals, suffixes and digit separators#
A value written directly in code is a literal. Its form decides its type:
Since C++14, an apostrophe can separate digits to make long numbers readable. It does not change the value:
#include <iostream>
int main() {
long long population = 1'428'627'663;
int mask = 0b1111'0000;
std::cout << population << ' ' << mask << ' ' << 0xFF << '\n';
}1428627663 240 255Common mistakes#
Practice#
Practice exercise
A cinema has 12 rows of 18 seats. Store both numbers as constexpr constants, calculate the total number of seats as another constexpr, and print how many seats remain if 150 tickets have been sold.
Show solution
#include <iostream>
int main() {
constexpr int rows = 12;
constexpr int seats_per_row = 18;
constexpr int total_seats = rows * seats_per_row;
int sold = 150;
std::cout << "Total: " << total_seats << '\n';
std::cout << "Remaining: " << total_seats - sold << '\n';
}Total: 216
Remaining: 66sold is an ordinary variable because the number of tickets sold changes while the cinema is open.
Key takeaway#
Finished this lesson?