Skip to content
C++

Compile-Time Programming Quick Reference

C++ compile-time programming cheat sheet — constexpr, consteval, constinit, if constexpr, concepts, type traits, template metaprogramming, and static_assert.

constexpr / consteval / constinit

cpp
// constexpr — may run at compile or runtime
constexpr int square(int n) { return n * n; }
constexpr int x = square(5);  // compile-time
int y = square(n);             // runtime OK

// consteval — MUST run at compile time
consteval int must_be_ct(int n) { return n * n; }
constexpr int a = must_be_ct(5);  // OK
// int b = must_be_ct(runtime_n);  // ERROR

// constinit — static variable initialized at compile time
constinit int counter = 0;  // no static init order fiasco

// if consteval (C++23)
constexpr double sqrt(double x) {
    if consteval { return ct_sqrt(x); }
    else         { return __builtin_sqrt(x); }
}

static_assert

cpp
static_assert(sizeof(int) == 4, "need 32-bit int");
static_assert(std::is_trivially_copyable_v<MyStruct>);

// In templates — fires when instantiated
template<typename T>
void serialize(T val) {
    static_assert(std::is_arithmetic_v<T>, "serialize: arithmetic types only");
}

// Dependent false (deferred error for else branch)
template<typename T>
void f() {
    if constexpr (std::is_integral_v<T>) { /* ... */ }
    else static_assert(!sizeof(T), "unsupported type");
}

Type Traits Quick Reference

cpp
#include <type_traits>

// Category queries
std::is_integral_v<T>          // int, char, bool, ...
std::is_floating_point_v<T>    // float, double, long double
std::is_arithmetic_v<T>        // integral || floating
std::is_pointer_v<T>
std::is_reference_v<T>
std::is_lvalue_reference_v<T>
std::is_rvalue_reference_v<T>
std::is_array_v<T>
std::is_enum_v<T>
std::is_class_v<T>             // struct or class (not union)
std::is_union_v<T>
std::is_void_v<T>
std::is_function_v<T>

// Relationship queries
std::is_same_v<T, U>
std::is_base_of_v<Base, Derived>
std::is_convertible_v<From, To>
std::is_constructible_v<T, Args...>
std::is_invocable_v<F, Args...>

// Special member functions
std::is_default_constructible_v<T>
std::is_copy_constructible_v<T>
std::is_move_constructible_v<T>
std::is_copy_assignable_v<T>
std::is_destructible_v<T>
std::is_trivially_copyable_v<T>    // safe for memcpy
std::is_standard_layout_v<T>
std::is_trivial_v<T>

// Modifiers
std::remove_cv_t<T>            // strip const/volatile
std::remove_reference_t<T>     // strip & or &&
std::remove_cvref_t<T>         // strip both (C++20)
std::add_const_t<T>
std::add_lvalue_reference_t<T>
std::add_pointer_t<T>
std::decay_t<T>                // array/function decay + remove_cv
std::underlying_type_t<Enum>   // enum's underlying integer type

// Conditionals
std::conditional_t<cond, T, F>        // cond ? T : F
std::enable_if_t<cond, T>             // SFINAE guard
std::type_identity_t<T>              // T (identity)
std::common_type_t<T, U>             // common type

// Inspection
std::is_constant_evaluated()         // in constexpr context? (C++20)

if constexpr

cpp
// Single function, multiple compile paths
template<typename T>
std::string describe(T x) {
    if constexpr (std::is_integral_v<T>)
        return std::format("int:{}", x);
    else if constexpr (std::is_floating_point_v<T>)
        return std::format("float:{:.4f}", x);
    else if constexpr (requires { x.to_string(); })
        return x.to_string();
    else
        return "[unknown]";
}

// Discarded branch not instantiated — may contain code invalid for other types
template<typename T>
auto get_size(T x) {
    if constexpr (std::ranges::range<T>) return x.size();
    else return sizeof(x);
}

Concepts (C++20)

cpp
// Define a concept
template<typename T>
concept Numeric = std::integral<T> || std::floating_point<T>;

template<typename T>
concept Printable = requires(T x) {
    { std::cout << x } -> std::same_as<std::ostream&>;
};

template<typename T>
concept Container = requires(T c) {
    c.begin(); c.end(); c.size(); typename T::value_type;
};

// Use in templates
template<Numeric T>
T square(T x) { return x * x; }

// Abbreviated syntax
auto square(Numeric auto x) { return x * x; }

// requires clause
template<typename T>
    requires std::is_arithmetic_v<T> && (sizeof(T) >= 4)
T safe_square(T x) { return x * x; }

Template Metaprogramming Patterns

cpp
// Type list
template<typename... Ts> struct TypeList {};
using Numbers = TypeList<int, long, float, double>;

// Recursive length
template<typename> struct Length;
template<typename... Ts>
struct Length<TypeList<Ts...>>
    : std::integral_constant<size_t, sizeof...(Ts)> {};

// Type at index (C++26: Ts...[I], C++20: recursive)
template<size_t I, typename... Ts>
struct TypeAt { using type = std::tuple_element_t<I, std::tuple<Ts...>>; };

// Filter types by predicate
template<template<typename> class Pred, typename... Ts>
using FilterT = /* ... */;  // complex — use mp11 or hana for production code

// Has member check (C++20 requires)
template<typename T>
concept HasSize = requires(T x) { { x.size() } -> std::convertible_to<size_t>; };

// Value sequences
template<int... Is>
using IntSeq = std::integer_sequence<int, Is...>;
auto seq = std::make_index_sequence<5>{};  // 0,1,2,3,4

// Expand index sequence
template<size_t... Is>
void expand(std::index_sequence<Is...>) {
    (process<Is>(), ...);  // fold expression
}

Variable Templates (C++14)

cpp
// Shorthand for type traits
template<typename T>
inline constexpr bool is_numeric_v = std::is_integral_v<T> || std::is_floating_point_v<T>;

// Mathematical constants
template<typename T>
inline constexpr T pi_v = T(3.14159265358979323846L);

// Use standard ones from <numbers> (C++20)
std::numbers::pi;         // double
std::numbers::pi_v<float>;

constexpr Data Structures (C++20)

cpp
// constexpr vector and string (C++20)
constexpr auto make_primes(int n) {
    std::vector<int> primes;
    for (int i = 2; i <= n; ++i) {
        bool prime = true;
        for (int j = 2; j * j <= i; ++j)
            if (i % j == 0) { prime = false; break; }
        if (prime) primes.push_back(i);
    }
    return primes;
}
constexpr auto primes = make_primes(20);  // {2,3,5,7,11,13,17,19}
static_assert(primes[3] == 7);

// constexpr map with std::array of pairs (sorted for binary search)
constexpr std::array<std::pair<std::string_view, int>, 3> kv = {{
    {"one", 1}, {"three", 3}, {"two", 2}
}};

FNV-1a Compile-Time Hash

cpp
consteval uint32_t fnv1a(std::string_view s) {
    uint32_t h = 2166136261u;
    for (char c : s) h = (h ^ uint8_t(c)) * 16777619u;
    return h;
}

// Switch on string at compile time
constexpr uint32_t h = fnv1a("hello");
switch (fnv1a(input)) {
    case fnv1a("get"):  /* ... */ break;
    case fnv1a("post"): /* ... */ break;
}
Edit on GitHub