콘텐츠로 이동

C++20 consteval — 즉시 함수(Immediate Function)

consteval은 C++20에서 도입된 즉시 함수(immediate function) 선언 키워드입니다. constexpr 함수는 런타임에도 호출될 수 있지만, 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);
}
}

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"

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

항목내용
런타임 호출항상 컴파일 오류
가상 함수consteval virtual 불가
함수 포인터consteval 함수를 가리키는 함수 포인터 생성 불가
예외 던지기컴파일 타임 오류 메시지로 활용 가능

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; } // 컴파일 타임 전용

// 실수 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