[2026] C++ Compile-Time Programming Complete Guide | constexpr, consteval, if constexpr & TMP
이 글의 핵심
Master C++ compile-time programming: constexpr, consteval, if constexpr, TMP, performance benchmarks, and practical patterns for zero-overhead runtime.
Introduction
C++ compile-time programming uses constexpr, consteval, and if constexpr to compute at compile time, eliminating runtime overhead. Analogy: Runtime computation is like cooking on order; compile-time computation is like pre-cooking and reheating—execution is instant.
What You’ll Learn
- Understand differences between constexpr, consteval, and if constexpr
- Distinguish compile-time vs runtime computation
- Compare with template metaprogramming (TMP)
- Apply practical patterns and avoid pitfalls
constexpr Basics
constexpr Variables
아래 코드는 cpp를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <iostream>
constexpr int MAX_SIZE = 100;
constexpr double PI = 3.14159;
int main() {
int arr[MAX_SIZE]; // Array size must be compile-time constant
std::cout << PI << std::endl;
return 0;
}
Key: constexpr variables can be used as array sizes and template arguments.
constexpr Functions
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <iostream>
// Can execute at compile time or runtime
constexpr int square(int x) {
return x * x;
}
int main() {
constexpr int result = square(5); // Compile-time
int arr[result]; // OK: compile-time constant
int x = 10;
int result2 = square(x); // Runtime (x is not constexpr)
std::cout << result << ", " << result2 << std::endl;
return 0;
}
Output:
25, 100
constexpr vs const
아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// const: runtime constant
const int x = 10;
// constexpr: compile-time constant
constexpr int y = 10;
int arr1[x]; // ❌ Error (const not guaranteed compile-time)
int arr2[y]; // ✅ OK (constexpr is compile-time)
Key: constexpr guarantees compile-time evaluation; const only guarantees immutability.
Practical Examples
1. constexpr Functions
아래 코드는 cpp를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <iostream>
constexpr int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
int main() {
constexpr int fact5 = factorial(5); // 120 (compile-time)
std::cout << fact5 << std::endl;
return 0;
}
Output:
120
Key: Recursive constexpr functions are evaluated at compile time when arguments are constexpr.
2. constexpr Classes
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <iostream>
class Point {
private:
int x_, y_;
public:
constexpr Point(int x, int y) : x_(x), y_(y) {}
constexpr int getX() const { return x_; }
constexpr int getY() const { return y_; }
constexpr int distanceSquared() const {
return x_ * x_ + y_ * y_;
}
};
int main() {
constexpr Point p(3, 4);
constexpr int dist = p.distanceSquared(); // 25 (compile-time)
int arr[dist]; // OK
std::cout << dist << std::endl;
return 0;
}
Output:
25
3. if constexpr (C++17)
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <iostream>
#include <type_traits>
template<typename T>
auto getValue(T t) {
if constexpr (std::is_pointer_v<T>) {
return *t; // Dereference if pointer
} else {
return t; // Return as-is
}
}
int main() {
int x = 10;
int* ptr = &x;
std::cout << getValue(x) << std::endl; // 10
std::cout << getValue(ptr) << std::endl; // 10
return 0;
}
Output:
10
10
Key: if constexpr enables compile-time branching based on type traits, eliminating dead code paths.
4. consteval (C++20)
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <iostream>
// Must execute at compile time only
consteval int sqr(int n) {
return n * n;
}
int main() {
constexpr int x = sqr(5); // ✅ OK (compile-time)
int y = 10;
// int z = sqr(y); // ❌ Error: runtime value
std::cout << x << std::endl;
return 0;
}
Output:
25
Key: consteval enforces compile-time-only execution, preventing runtime calls.
Advanced Patterns
1. Compile-Time String Hashing
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <iostream>
constexpr unsigned int hash(const char* str) {
unsigned int hash = 5381;
while (*str) {
hash = ((hash << 5) + hash) + (*str++);
}
return hash;
}
int main() {
constexpr unsigned int startHash = hash("start");
constexpr unsigned int stopHash = hash("stop");
const char* command = "start";
switch (hash(command)) {
case startHash:
std::cout << "Start" << std::endl;
break;
case stopHash:
std::cout << "Stop" << std::endl;
break;
default:
std::cout << "Unknown" << std::endl;
}
return 0;
}
Output:
Start
Key: Compile-time hashing enables fast string-based switch statements.
2. Compile-Time Array Generation
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <array>
#include <iostream>
template<size_t N>
constexpr auto generateFibonacci() {
std::array<int, N> result{};
if (N > 0) result[0] = 0;
if (N > 1) result[1] = 1;
for (size_t i = 2; i < N; ++i) {
result[i] = result[i - 1] + result[i - 2];
}
return result;
}
int main() {
constexpr auto fib = generateFibonacci<10>();
for (int x : fib) {
std::cout << x << " "; // 0 1 1 2 3 5 8 13 21 34
}
std::cout << std::endl;
return 0;
}
Output:
0 1 1 2 3 5 8 13 21 34
Key: Compile-time array generation creates lookup tables with zero runtime cost.
3. Template Metaprogramming (TMP)
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <iostream>
// Recursive template
template<int N>
struct Factorial {
static constexpr int value = N * Factorial<N - 1>::value;
};
template<>
struct Factorial<0> {
static constexpr int value = 1;
};
int main() {
std::cout << Factorial<5>::value << std::endl; // 120 (compile-time)
return 0;
}
Output:
120
Key: TMP uses template recursion for compile-time computation. Prefer constexpr functions for readability.
Performance Comparison
Benchmark: Fibonacci
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <chrono>
#include <iostream>
// Runtime
int runtimeFib(int n) {
if (n <= 1) return n;
return runtimeFib(n - 1) + runtimeFib(n - 2);
}
// Compile-time
constexpr int compiletimeFib(int n) {
if (n <= 1) return n;
return compiletimeFib(n - 1) + compiletimeFib(n - 2);
}
int main() {
// Runtime (slow)
auto start = std::chrono::high_resolution_clock::now();
int r = runtimeFib(40);
auto end = std::chrono::high_resolution_clock::now();
auto time = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
std::cout << "Runtime: " << time << "ms" << std::endl;
// Compile-time (instant)
constexpr int c = compiletimeFib(40);
std::cout << "Compile-time: 0ms (pre-computed)" << std::endl;
return 0;
}
Results:
| Method | Time |
|---|---|
| Runtime | 1200ms |
| Compile-time | 0ms |
| Conclusion: Compile-time computation is dramatically faster at runtime (but increases build time). |
Production Patterns
Pattern 1: Lookup Table Generation
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <array>
#include <iostream>
#include <cmath>
constexpr auto generateSinTable() {
constexpr size_t SIZE = 360;
std::array<double, SIZE> table{};
constexpr double PI = 3.14159265358979323846;
for (size_t i = 0; i < SIZE; ++i) {
double radians = i * PI / 180.0;
// In real code: table[i] = std::sin(radians);
// (std::sin is not constexpr in C++17, but is in C++26)
table[i] = radians;
}
return table;
}
int main() {
constexpr auto sinTable = generateSinTable();
std::cout << sinTable[45] << std::endl; // 0.785398 (45 degrees)
return 0;
}
Key: Pre-compute trigonometric tables at compile time for real-time graphics/physics.
Pattern 2: Type Checking
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <iostream>
#include <type_traits>
template<typename T>
constexpr bool isNumeric() {
if constexpr (std::is_integral_v<T> || std::is_floating_point_v<T>) {
return true;
} else {
return false;
}
}
template<typename T>
void process(T value) {
if constexpr (isNumeric<T>()) {
std::cout << "Number: " << value << std::endl;
} else {
std::cout << "String: " << value << std::endl;
}
}
int main() {
process(42); // Number: 42
process("hello"); // String: hello
return 0;
}
Output:
Number: 42
String: hello
Pattern 3: Compile-Time Sorting
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <algorithm>
#include <array>
#include <iostream>
constexpr void bubbleSort(int* arr, int n) {
for (int i = 0; i < n - 1; ++i) {
for (int j = 0; j < n - i - 1; ++j) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
constexpr auto getSortedArray() {
std::array<int, 5> arr = {5, 2, 8, 1, 9};
bubbleSort(arr.data(), arr.size());
return arr;
}
int main() {
constexpr auto sorted = getSortedArray();
for (int x : sorted) {
std::cout << x << " "; // 1 2 5 8 9
}
std::cout << std::endl;
return 0;
}
Output:
1 2 5 8 9
Pattern 4: Bit Manipulation
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <iostream>
constexpr unsigned int reverseBits(unsigned int n) {
unsigned int result = 0;
for (int i = 0; i < 32; ++i) {
result <<= 1;
result |= (n & 1);
n >>= 1;
}
return result;
}
int main() {
constexpr unsigned int reversed = reverseBits(0b10110000);
std::cout << std::hex << reversed << std::endl;
return 0;
}
Key: Compile-time bit manipulation for embedded systems and cryptography.
Common Issues
Issue 1: constexpr Constraints
다음은 cpp를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// ❌ Static variables not allowed (pre-C++23)
constexpr int bad() {
static int x = 0; // Error
return x++;
}
// ❌ Dynamic allocation not allowed (C++17)
constexpr int bad2() {
int* p = new int(10); // Error
return *p;
}
// ✅ OK
constexpr int good(int x) {
return x * 2;
}
Key: constexpr functions have restrictions—no static locals (pre-C++23), limited dynamic allocation.
Issue 2: Runtime Values
아래 코드는 cpp를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <iostream>
int main() {
int x;
std::cin >> x;
// ❌ Error: runtime value
constexpr int y = x * 2;
// ✅ OK: runtime variable
int y2 = x * 2;
return 0;
}
Key: constexpr requires compile-time known values.
Issue 3: Complex Computation
다음은 cpp를 활용한 상세한 구현 코드입니다. 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// ❌ Slow compile time
constexpr int slowFib(int n) {
if (n <= 1) return n;
return slowFib(n - 1) + slowFib(n - 2);
}
constexpr int x = slowFib(50); // Very slow compilation!
// ✅ Memoization
constexpr auto fastFib() {
std::array<int, 50> result{};
result[0] = 0;
result[1] = 1;
for (int i = 2; i < 50; ++i) {
result[i] = result[i - 1] + result[i - 2];
}
return result;
}
constexpr auto fibTable = fastFib();
constexpr int y = fibTable[49]; // Fast
Key: Use iterative algorithms or memoization to avoid exponential compile-time complexity.
Issue 4: constexpr vs consteval Confusion
다음은 cpp를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// constexpr: compile-time or runtime
constexpr int square(int n) {
return n * n;
}
int x = 10;
int y = square(x); // ✅ Runs at runtime
// consteval: compile-time only
consteval int sqr(int n) {
return n * n;
}
constexpr int z = sqr(5); // ✅ OK
// int w = sqr(x); // ❌ Error: runtime value
Key: Use consteval to enforce compile-time-only intent.
Template Metaprogramming (TMP)
Recursive Templates
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <iostream>
// Recursive template
template<int N>
struct Factorial {
static constexpr int value = N * Factorial<N - 1>::value;
};
template<>
struct Factorial<0> {
static constexpr int value = 1;
};
int main() {
std::cout << Factorial<5>::value << std::endl; // 120 (compile-time)
return 0;
}
Output:
120
Key: TMP uses template specialization for compile-time recursion. Prefer constexpr functions for readability.
TMP vs constexpr
아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// TMP (verbose)
template<int N>
struct Factorial {
static constexpr int value = N * Factorial<N - 1>::value;
};
// constexpr (readable)
constexpr int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
Recommendation: Use constexpr functions for most compile-time computation; reserve TMP for type-level programming.
Production Use Cases
Use Case 1: Configuration Tables
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <array>
constexpr auto generateConfigTable() {
std::array<int, 256> table{};
for (int i = 0; i < 256; ++i) {
table[i] = i * 2; // Example: multiply by 2
}
return table;
}
constexpr auto configTable = generateConfigTable();
int main() {
int value = configTable[42]; // Instant lookup
}
Key: Pre-compute configuration tables for embedded systems.
Use Case 2: Type Dispatch
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <iostream>
#include <type_traits>
template<typename T>
void process(T value) {
if constexpr (std::is_integral_v<T>) {
std::cout << "Integer: " << value << std::endl;
} else if constexpr (std::is_floating_point_v<T>) {
std::cout << "Float: " << value << std::endl;
} else {
std::cout << "Other: " << value << std::endl;
}
}
int main() {
process(42); // Integer: 42
process(3.14); // Float: 3.14
process("hello"); // Other: hello
return 0;
}
Output:
Integer: 42
Float: 3.14
Other: hello
Use Case 3: Compile-Time Validation
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <array>
template<size_t N>
constexpr bool isPowerOfTwo() {
return N > 0 && (N & (N - 1)) == 0;
}
template<size_t N>
class Buffer {
static_assert(isPowerOfTwo<N>(), "Buffer size must be power of 2");
std::array<char, N> data;
};
int main() {
Buffer<256> buf1; // ✅ OK
// Buffer<100> buf2; // ❌ Error: not power of 2
}
Key: Use static_assert with constexpr functions for compile-time validation.
Performance Benchmarks
Compile-Time vs Runtime
// Benchmark: Fibonacci(40)
// Runtime: 1200ms
// Compile-time: 0ms (pre-computed)
Key: Compile-time computation eliminates runtime overhead but increases build time.
Compile Time Impact
아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// Simple constexpr: +0.1s compile time
constexpr int x = factorial(10);
// Complex constexpr: +5s compile time
constexpr auto table = generateLargeTable<10000>();
Trade-off: Balance compile-time work with build time constraints.
Best Practices
1. Use constexpr for Pure Functions
아래 코드는 cpp를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// ✅ Pure function: good constexpr candidate
constexpr int add(int a, int b) {
return a + b;
}
// ❌ Side effects: not constexpr
int addAndLog(int a, int b) {
std::cout << "Adding" << std::endl; // I/O not allowed
return a + b;
}
2. Prefer constexpr Over TMP
아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// ❌ TMP (verbose)
template<int N>
struct Factorial {
static constexpr int value = N * Factorial<N - 1>::value;
};
// ✅ constexpr (readable)
constexpr int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
3. Use consteval for Compile-Time-Only Intent
아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// ✅ Enforce compile-time
consteval int configValue(int id) {
return id * 100;
}
constexpr int x = configValue(5); // OK
// int y = configValue(runtimeValue); // Error
4. Avoid Deep Recursion
다음은 cpp를 활용한 상세한 구현 코드입니다. 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// ❌ Deep recursion (slow compile)
constexpr int fib(int n) {
return n <= 1 ? n : fib(n-1) + fib(n-2);
}
// ✅ Iterative (fast compile)
constexpr int fib(int n) {
int a = 0, b = 1;
for (int i = 0; i < n; ++i) {
int temp = a + b;
a = b;
b = temp;
}
return a;
}
5. Use static_assert for Documentation
아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
template<typename T>
void process(T value) {
static_assert(std::is_arithmetic_v<T>, "T must be numeric");
// ...
}
Summary
Key Points
- constexpr
- Compile-time or runtime execution
- Variables, functions, classes
- Array sizes, template arguments
- consteval (C++20)
- Compile-time-only execution
- Immediate functions
- No runtime values allowed
- if constexpr (C++17)
- Compile-time branching
- Type-based conditional compilation
- Replaces template specialization
- Performance
- Compile-time: zero runtime overhead
- Compile time may increase
- Use memoization for complex computation
Selection Guide
| Scenario | Recommendation | Reason |
|---|---|---|
| Compile-time constant | constexpr | Array sizes, etc. |
| Compile-time-only | consteval | Clear intent |
| Type-based branching | if constexpr | Conditional compilation |
| Runtime also needed | constexpr | Flexibility |
Cheat Sheet
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// constexpr variable
constexpr int MAX_SIZE = 100;
// constexpr function
constexpr int square(int x) {
return x * x;
}
// constexpr class
class Point {
public:
constexpr Point(int x, int y) : x_(x), y_(y) {}
constexpr int getX() const { return x_; }
private:
int x_, y_;
};
// if constexpr
template<typename T>
auto getValue(T t) {
if constexpr (std::is_pointer_v<T>) {
return *t;
} else {
return t;
}
}
// consteval
consteval int sqr(int n) {
return n * n;
}
Compile-Time Programming Checklist
- Pure functions marked constexpr?
- Using consteval for compile-time-only intent?
- Avoiding deep recursion?
- Using static_assert for validation?
- Balancing compile time vs runtime performance?
Related Articles
- C++ constexpr Functions Guide
- C++ if constexpr Guide
- C++ constexpr Basics Series #26-1
- C++ Compile-Time Optimization #15-3
Keywords
C++ constexpr, consteval, compile-time programming, template metaprogramming, TMP, if constexpr, immediate functions One-line summary: Compile-time programming with constexpr, consteval, and if constexpr eliminates runtime overhead by pre-computing values and enabling type-based conditional compilation.