Constants and constexpr

Values that shouldn't change deserve to be marked as such. C++ has const for read-only values and constexpr for values — and functions — the compiler can evaluate before the program even runs.

const

C++
int main() {
    const double vat_rate = 0.20;
    vat_rate = 0.25;
    return 0;
}
Compiler error
main.cpp:3:14: error: cannot assign to variable 'vat_rate' with const-qualified type 'const double'

A const variable must be initialised and can never be changed. Its value can come from anything, including run-time input.

constexpr

constexpr goes further: the value must be known at compile time. That lets you use it anywhere a compile-time constant is required, such as array sizes and template arguments:

C++
#include <array>
#include <iostream>

constexpr int board_size = 8;
constexpr double pi = 3.14159265358979;

int main() {
    std::array<int, board_size> row{};
    constexpr double area = pi * 2.0 * 2.0;
    std::cout << row.size() << " squares, circle area " << area << '\n';
}
Output
8 squares, circle area 12.5664

Prefer constexpr for true constants and const for values computed at run time that shouldn't change afterwards. Both beat #define macros, which have no type and ignore scope.

constexpr functions

A constexpr function can run at compile time when its arguments are constants, and at run time otherwise:

C++
#include <iostream>

constexpr long long factorial(int n) {
    long long result = 1;
    for (int i = 2; i <= n; ++i) result *= i;
    return result;
}

int main() {
    constexpr long long f10 = factorial(10);    // computed by the compiler
    static_assert(f10 == 3628800);               // checked at compile time
    int n = 15;
    std::cout << f10 << ' ' << factorial(n) << '\n';   // run time
}
Output
3628800 1307674368000

static_assert checks a condition while compiling — a failed one is a compile error, so the bug never reaches a running program.

consteval

C++20's consteval marks a function that must run at compile time. Calling it with a run-time value is an error:

C++
consteval int square(int x) { return x * x; }

int main() {
    constexpr int a = square(12);   // fine
    int n = 5;
    int b = square(n);              // error: n isn't a constant
    return a + b;
}
Compiler error
main.cpp:6:13: error: call to consteval function 'square' is not a constant expression

Enumerations for related constants

For a fixed set of named choices, an enum class is better than a group of integer constants (lesson 20 covers it fully):

C++
#include <iostream>

enum class Size { small, medium, large };

int main() {
    Size s = Size::medium;
    if (s == Size::medium) std::cout << "medium selected\n";
}
Output
medium selected

Avoid magic numbers

A literal like 86400 in the middle of code forces the reader to guess its meaning. A named constant documents it and lets you change it in one place:

C++
#include <iostream>

constexpr int seconds_per_minute = 60;
constexpr int seconds_per_hour = 60 * seconds_per_minute;
constexpr int seconds_per_day = 24 * seconds_per_hour;

int main() {
    int t = 200'000;
    std::cout << t / seconds_per_day << " d " << t % seconds_per_day / seconds_per_hour << " h "
              << t % seconds_per_hour / seconds_per_minute << " min " << t % seconds_per_minute << " s\n";
}
Output
2 d 7 h 33 min 20 s

Try it yourself

Write a constexpr function that converts Celsius to Fahrenheit, and use static_assert to check that 100 °C is 212 °F.

Try it yourself · main.cpp
Edit the code, copy it into main.cpp, then compile and run it with c++ -std=c++20 -Wall main.cpp -o main && ./main. How to set up a C++ compiler
Output
-10 C = 14 F
0 C = 32 F
21.5 C = 70.7 F
37 C = 98.6 F