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;
}