[2026] C++ 컴파일 타임 프로그래밍 | constexpr·consteval·if constexpr 완벽 가이드
이 글의 핵심
C++ 컴파일 타임 프로그래밍: constexpr·consteval·if constexpr constexpr 기초·constexpr 변수·constexpr 클래스·템플릿 메타프로그래밍.
들어가며
C++의 컴파일 타임 프로그래밍은 constexpr, consteval, if constexpr을 사용해 컴파일 타임에 계산하고, 런타임 성능을 높입니다. 비유로 말씀드리면, 런타임 계산은 주문이 들어올 때마다 요리하는 것, 컴파일 타임 계산은 미리 요리해 놓고 데우기만 하는 것에 가깝습니다. 미리 계산해 놓으면 실행 시간이 줄어듭니다.
이 글을 읽으면
- constexpr, consteval, if constexpr의 차이를 이해합니다
- 컴파일 타임 계산과 런타임 계산을 구분합니다
- 템플릿 메타프로그래밍과 비교합니다
- 실무 활용법과 주의사항을 파악합니다
실무에서 마주한 현실
개발을 배울 때는 모든 게 깔끔하고 이론적입니다. 하지만 실무는 다릅니다. 레거시 코드와 씨름하고, 급한 일정에 쫓기고, 예상치 못한 버그와 마주합니다. 이 글에서 다루는 내용도 처음엔 이론으로 배웠지만, 실제 프로젝트에 적용하면서 “아, 이래서 이렇게 설계하는구나” 하고 깨달은 것들입니다. 특히 기억에 남는 건 첫 프로젝트에서 겪은 시행착오입니다. 책에서 배운 대로 했는데 왜 안 되는지 몰라 며칠을 헤맸죠. 결국 선배 개발자의 코드 리뷰를 통해 문제를 발견했고, 그 과정에서 많은 걸 배웠습니다. 이 글에서는 이론뿐 아니라 실전에서 마주칠 수 있는 함정들과 해결 방법을 함께 다루겠습니다.
목차
constexpr 기초
constexpr 변수
아래 코드는 cpp를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <iostream>
constexpr int MAX_SIZE = 100;
constexpr double PI = 3.14159;
int main() {
int arr[MAX_SIZE]; // 배열 크기로 사용 가능
std::cout << PI << std::endl;
return 0;
}
constexpr 함수
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <iostream>
// 컴파일 타임 또는 런타임에 실행 가능
constexpr int square(int x) {
return x * x;
}
int main() {
constexpr int result = square(5); // 컴파일 타임에 계산
int arr[result]; // 배열 크기로 사용 가능
int x = 10;
int result2 = square(x); // 런타임에도 사용 가능
std::cout << result << ", " << result2 << std::endl;
return 0;
}
constexpr vs const
아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// const: 런타임 상수
const int x = 10;
// constexpr: 컴파일 타임 상수
constexpr int y = 10;
int arr1[x]; // ❌ 에러 (const는 배열 크기로 불가)
int arr2[y]; // ✅ OK (constexpr는 가능)
실전 구현
1) constexpr 함수
아래 코드는 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 (컴파일 타임)
std::cout << fact5 << std::endl;
return 0;
}
2) constexpr 클래스
다음은 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 (컴파일 타임)
int arr[dist]; // OK
std::cout << dist << std::endl;
return 0;
}
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; // 포인터면 역참조
} else {
return t; // 아니면 그대로
}
}
int main() {
int x = 10;
int* ptr = &x;
std::cout << getValue(x) << std::endl; // 10
std::cout << getValue(ptr) << std::endl; // 10
return 0;
}
4) consteval (C++20)
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <iostream>
// 반드시 컴파일 타임에만 실행
consteval int sqr(int n) {
return n * n;
}
int main() {
constexpr int x = sqr(5); // ✅ OK
int y = 10;
// int z = sqr(y); // ❌ 에러: 런타임 값
std::cout << x << std::endl;
return 0;
}
고급 활용
1) 컴파일 타임 문자열 해시
다음은 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 << "시작" << std::endl;
break;
case stopHash:
std::cout << "정지" << std::endl;
break;
default:
std::cout << "알 수 없음" << std::endl;
}
return 0;
}
2) 컴파일 타임 배열 생성
다음은 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;
}
3) 템플릿 메타프로그래밍
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <iostream>
// 재귀 템플릿
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 (컴파일 타임)
return 0;
}
성능 비교
벤치마크: 피보나치
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <chrono>
#include <iostream>
// 런타임
int runtimeFib(int n) {
if (n <= 1) return n;
return runtimeFib(n - 1) + runtimeFib(n - 2);
}
// 컴파일 타임
constexpr int compiletimeFib(int n) {
if (n <= 1) return n;
return compiletimeFib(n - 1) + compiletimeFib(n - 2);
}
int main() {
// 런타임 계산 (느림)
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 << "런타임: " << time << "ms" << std::endl;
// 컴파일 타임 계산 (즉시)
constexpr int c = compiletimeFib(40);
std::cout << "컴파일 타임: 0ms (이미 계산됨)" << std::endl;
return 0;
}
결과:
| 방법 | 시간 |
|---|---|
| 런타임 | 1200ms |
| 컴파일 타임 | 0ms |
| 결론: 컴파일 타임 계산이 압도적으로 빠름 |
실무 사례
사례 1: 룩업 테이블 생성
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <array>
#include <iostream>
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;
table[i] = radians; // 실제로는 sin 계산
}
return table;
}
int main() {
constexpr auto sinTable = generateSinTable();
std::cout << sinTable[45] << std::endl; // 0.785398 (45도)
return 0;
}
사례 2: 타입 체크
다음은 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 << "숫자: " << value << std::endl;
} else {
std::cout << "문자열: " << value << std::endl;
}
}
int main() {
process(42); // 숫자: 42
process("hello"); // 문자열: hello
return 0;
}
사례 3: 컴파일 타임 정렬
다음은 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;
}
사례 4: 비트 연산 최적화
다음은 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;
}
트러블슈팅
문제 1: constexpr 제약
증상: 컴파일 에러 다음은 cpp를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// ❌ static 변수 불가
constexpr int bad() {
static int x = 0; // 에러
return x++;
}
// ❌ 동적 할당 불가 (C++17)
constexpr int bad2() {
int* p = new int(10); // 에러
return *p;
}
// ✅ OK
constexpr int good(int x) {
return x * 2;
}
문제 2: 런타임 값 사용
증상: 컴파일 에러 아래 코드는 cpp를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <iostream>
int main() {
int x;
std::cin >> x;
// ❌ 에러: 런타임 값
constexpr int y = x * 2;
// ✅ OK: 런타임 변수
int y2 = x * 2;
return 0;
}
문제 3: 복잡한 계산
증상: 컴파일 시간 증가 다음은 cpp를 활용한 상세한 구현 코드입니다. 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// ❌ 컴파일 시간 증가
constexpr int slowFib(int n) {
if (n <= 1) return n;
return slowFib(n - 1) + slowFib(n - 2);
}
constexpr int x = slowFib(50); // 컴파일 매우 느림!
// ✅ 메모이제이션
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]; // 빠름
문제 4: constexpr vs consteval 혼동
증상: 의도와 다른 동작 다음은 cpp를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// constexpr: 컴파일 타임 또는 런타임
constexpr int square(int n) {
return n * n;
}
int x = 10;
int y = square(x); // ✅ 런타임에 실행
// consteval: 컴파일 타임만
consteval int sqr(int n) {
return n * n;
}
constexpr int z = sqr(5); // ✅ OK
// int w = sqr(x); // ❌ 에러: 런타임 값
마무리
컴파일 타임 프로그래밍은 constexpr, consteval, if constexpr을 사용해 런타임 성능을 높입니다.
핵심 요약
- constexpr
- 컴파일 타임 또는 런타임에 실행
- 변수, 함수, 클래스에 사용
- 배열 크기, 템플릿 인자로 사용 가능
- consteval (C++20)
- 컴파일 타임에만 실행
- 즉시 함수
- 런타임 값 사용 불가
- if constexpr (C++17)
- 컴파일 타임 분기
- 타입에 따른 조건부 컴파일
- 템플릿 특수화 대체
- 성능
- 컴파일 타임 계산: 런타임 오버헤드 없음
- 컴파일 시간 증가 가능
- 복잡한 계산은 메모이제이션
선택 가이드
| 상황 | 권장 | 이유 |
|---|---|---|
| 컴파일 타임 상수 | constexpr | 배열 크기 등 |
| 컴파일 타임 전용 | consteval | 의도 명확 |
| 타입별 분기 | if constexpr | 조건부 컴파일 |
| 런타임도 필요 | constexpr | 유연성 |
코드 예제 치트시트
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// constexpr 변수
constexpr int MAX_SIZE = 100;
// constexpr 함수
constexpr int square(int x) {
return x * x;
}
// constexpr 클래스
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;
}
다음 단계
- constexpr 함수: C++ constexpr 함수
- constexpr if: C++ constexpr if
- constexpr 기초: C++ constexpr 기초
참고 자료
- “C++20 The Complete Guide” - Nicolai M. Josuttis
- “Effective Modern C++” - Scott Meyers
- cppreference: https://en.cppreference.com/w/cpp/language/constexpr 한 줄 정리: constexpr로 컴파일 타임에 계산하고, consteval로 컴파일 타임 전용 함수를 만들며, if constexpr로 타입별 조건부 컴파일을 수행한다.
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- C++ constexpr 함수 | “컴파일 타임 함수” 가이드
- C++ constexpr if | “컴파일 타임 분기” 가이드
- C++ constexpr 함수와 변수 | 컴파일 타임에 계산하기 [#26-1]