SkillByAIOpen interactive version →

Lesson 18 / 25

auto, decltype, constexpr and Compile-Time Programming

Use type deduction and compile-time evaluation effectively.

Letting the compiler do the work

Modern C++ moves more work to the compiler. auto deduces variable types and, for functions, return types. decltype(expr) yields the type of an expression, useful in generic code. constexpr functions can be evaluated at compile time when their arguments are constants, and since C++20 they can use loops, local variables, std::vector and std::string inside the evaluation. consteval (C++20) functions must run at compile time, and constinit guarantees a variable is initialised at compile time. static_assert checks conditions during compilation. if constexpr discards branches that do not apply to the instantiated type. Type traits in <type_traits> (std::is_integral_v<T>, std::remove_cvref_t<T>) inspect and transform types. Together these enable lookup tables computed at build time, configuration checked at compile time and generic code without run-time overhead. Use them when they simplify code or catch errors early, not merely to be clever.

Compile-time computation and checks

The table is computed by the compiler; static_assert verifies it.

#include <array>
#include <cstddef>
#include <string>
#include <type_traits>

constexpr std::array<unsigned long long, 21> make_factorials() {
    std::array<unsigned long long, 21> f{};
    f[0] = 1;
    for (std::size_t i = 1; i < f.size(); ++i) f[i] = f[i - 1] * i;   // loops allowed in constexpr
    return f;
}

constexpr auto factorials = make_factorials();                // computed at compile time
static_assert(factorials[5] == 120);
static_assert(factorials[20] == 2'432'902'008'176'640'000ULL);

consteval int kib(int n) { return n * 1024; }               // must be compile-time
constexpr int buffer_size = kib(64);

template <typename T>
std::string describe(const T& value) {
    if constexpr (std::is_integral_v<T>)            return "integer " + std::to_string(value);
    else if constexpr (std::is_floating_point_v<T>) return "real " + std::to_string(value);
    else                                            return "something else";
}

constexpr is permission, not a promise

A constexpr function can still run at run time when given run-time values. If evaluation must happen at compile time, use the result in a constexpr variable, a static_assert, or mark the function consteval.

Quick check: What does consteval guarantee about a function?

  • Every call is evaluated at compile time
  • It runs only at run time
  • It is inlined
  • It cannot use loops
Answer

Every call is evaluated at compile time — consteval functions are immediate functions: each call must produce a compile-time constant.