C++20 consteval — 즉시 함수(Immediate Function)
consteval은 C++20에서 도입된 즉시 함수(immediate function) 선언 키워드입니다. constexpr 함수는 런타임에도 호출될 수 있지만, consteval 함수는 반드시 컴파일 타임에만 호출됩니다. 런타임 호출 시도는 컴파일 오류가 됩니다.
1. constexpr vs consteval 비교
섹션 제목: “1. constexpr vs consteval 비교”#include <cstdint>
// constexpr: 런타임·컴파일 타임 모두 가능constexpr int square_cx(int x) { return x * x; }
// consteval: 컴파일 타임 전용consteval int square_ce(int x) { return x * x; }
int main() { constexpr int a = square_cx(5); // OK — 컴파일 타임 int b = square_cx(5); // OK — 런타임도 가능
constexpr int c = square_ce(5); // OK — 컴파일 타임 // int d = square_ce(5); // ERROR: 런타임 호출 불가}2. 기본 활용: 컴파일 타임 테이블 생성
섹션 제목: “2. 기본 활용: 컴파일 타임 테이블 생성”#include <array>#include <cstddef>
consteval std::array<int, 10> make_squares() { std::array<int, 10> arr{}; for (std::size_t i = 0; i < 10; ++i) arr[i] = static_cast<int>(i * i); return arr;}
// 바이너리에 상수 배열이 직접 삽입됨inline constexpr auto squares = make_squares();3. consteval if — 조건부 컴파일 타임 분기
섹션 제목: “3. consteval if — 조건부 컴파일 타임 분기”C++23에서 도입된 if consteval을 사용하면 함수 내에서 컴파일 타임 여부를 분기할 수 있습니다.
#include <cmath>
constexpr double my_sqrt(double x) { if consteval { // 컴파일 타임 경로 (단순 뉴턴법) double r = x; for (int i = 0; i < 64; ++i) r = (r + x / r) / 2.0; return r; } else { // 런타임 경로 (하드웨어 sqrt 사용) return std::sqrt(x); }}4. 정책 검증에 활용
섹션 제목: “4. 정책 검증에 활용”consteval void validate_buffer_size(std::size_t size) { if (size == 0 || (size & (size - 1)) != 0) throw "Buffer size must be a non-zero power of 2";}
template<std::size_t N>struct Buffer { consteval Buffer() { validate_buffer_size(N); } int data[N];};
// Buffer<128> ok; // OK// Buffer<100> err; // 컴파일 오류: "Buffer size must be a non-zero power of 2"5. consteval 생성자
섹션 제목: “5. consteval 생성자”struct Color { uint8_t r, g, b, a;
consteval Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255) : r(r), g(g), b(b), a(a) {}};
// 모든 Color 리터럴은 컴파일 타임에 검증됨constexpr Color red = Color(255, 0, 0);constexpr Color white = Color(255, 255, 255);6. 주요 제약 사항
섹션 제목: “6. 주요 제약 사항”| 항목 | 내용 |
|---|---|
| 런타임 호출 | 항상 컴파일 오류 |
| 가상 함수 | consteval virtual 불가 |
| 함수 포인터 | consteval 함수를 가리키는 함수 포인터 생성 불가 |
| 예외 던지기 | 컴파일 타임 오류 메시지로 활용 가능 |
7. 문자열 리터럴 해시
섹션 제목: “7. 문자열 리터럴 해시”consteval은 문자열 리터럴을 컴파일 타임 정수 ID로 변환하는 데 자주 사용됩니다.
#include <cstdint>
// FNV-1a 해시 — 컴파일 타임 전용consteval uint32_t operator""_hash(const char* str, std::size_t len){ uint32_t hash = 2166136261u; for (std::size_t i = 0; i < len; ++i) { hash ^= static_cast<uint32_t>(str[i]); hash *= 16777619u; } return hash;}
// 사용constexpr uint32_t id_player = "PlayerCharacter"_hash; // 컴파일 타임 상수constexpr uint32_t id_monster = "Monster"_hash;
// switch에서 문자열 비교 (런타임에도 상수 비교)void HandleEvent(uint32_t event_id){ switch (event_id) { case "PlayerCharacter"_hash: /* ... */ break; case "Monster"_hash: /* ... */ break; }}8. consteval 함수 내 컴파일 타임 단언
섹션 제목: “8. consteval 함수 내 컴파일 타임 단언”#include <stdexcept>
// consteval 함수에서 throw는 컴파일 오류 메시지로 사용됨consteval int checked_shift(int value, int bits){ if (bits < 0 || bits >= 32) throw std::invalid_argument("shift amount must be 0-31"); return value << bits;}
constexpr int a = checked_shift(1, 4); // OK: 16// constexpr int b = checked_shift(1, 32); // 컴파일 오류: "shift amount must be 0-31"9. constexpr / consteval / constinit / const 비교
섹션 제목: “9. constexpr / consteval / constinit / const 비교”| 키워드 | 적용 대상 | 평가 시점 | 런타임 변경 | 주요 용도 |
|---|---|---|---|---|
const | 변수 | 컴파일 또는 런타임 | 불가 | 불변 값 |
constexpr 변수 | 변수 | 컴파일 타임 | 불가 | 컴파일 타임 상수 |
constexpr 함수 | 함수 | 컴파일 또는 런타임 | — | 양쪽 모두 사용 가능한 함수 |
consteval 함수 | 함수 | 반드시 컴파일 타임 | — | 컴파일 타임 전용 계산·검증 |
constinit | 정적 변수 | 컴파일 타임 초기화 | 가능 | 초기화 순서 문제 방지 |
// 각 키워드의 의도 명확화 예시const int a = 42; // 불변이지만 반드시 컴파일 타임은 아님constexpr int b = 42; // 컴파일 타임 상수constinit int c = 42; // 컴파일 타임 초기화, 이후 수정 가능// constinit const int d = 42; // 컴파일 타임 초기화 + 불변
constexpr int double_cx(int x) { return x * 2; } // 런타임 가능consteval int double_ce(int x) { return x * 2; } // 컴파일 타임 전용10. 자주 하는 실수
섹션 제목: “10. 자주 하는 실수”// 실수 1: consteval 함수를 함수 포인터로 저장 시도consteval int compute(int x) { return x * x; }
// auto fp = &compute; // 오류: consteval 함수의 주소를 취할 수 없음
// 실수 2: consteval 함수를 런타임 인자로 호출void runtime_fn(int x){ // int r = compute(x); // 오류: x는 런타임 값 constexpr int r = compute(5); // OK: 5는 상수}
// 실수 3: consteval과 virtual 혼용struct Base { // consteval virtual int foo() = 0; // 오류: consteval은 virtual 불가 virtual int foo() = 0;};consteval은 컴파일 타임 계산이 반드시 보장되어야 하는 상황에 사용합니다. 상수 테이블 생성, 입력 검증 강제, 설정 값 정책 확인 등에서 constexpr보다 강력한 안전망을 제공합니다.
선택 기준:
- 런타임에도 호출될 수 있어야 한다 →
constexpr - 반드시 컴파일 타임에만 실행돼야 한다 (검증, 테이블 생성) →
consteval - 정적 변수를 컴파일 타임에 초기화하되 나중에 수정한다 →
constinit