C++20 consteval: constexpr와 달리 컴파일 타임 실행을 강제하는 법

이 글의 핵심

consteval이 constexpr와 어떻게 다른지, 즉시 함수의 규칙, 문자열·설정값을 컴파일 타임에 검증하는 활용법을 컴파일 타임 설정 시스템 예제로 정리합니다.

컴파일 타임 실행을 강제해야 하는 이유

constexpr 함수는 런타임에도 실행될 수 있다

문제: constexpr 함수는 컴파일 타임에도, 런타임에도 실행될 수 있습니다. 컴파일 타임 계산을 강제하고 싶을 때 모호합니다.

constexpr int factorial(int n) {
    return n <= 1 ? 1 : n * factorial(n - 1);
}
int main() {
    constexpr int x = factorial(5);  // 컴파일 타임: 120
    
    int n = 10;
    int y = factorial(n);  // 런타임 (의도하지 않았을 수도)
}

해결: consteval은 컴파일 타임 전용 함수입니다. 런타임 값을 넘기면 컴파일 에러가 나므로, 컴파일 타임 계산을 보장할 수 있습니다.

consteval int factorial(int n) {
    return n <= 1 ? 1 : n * factorial(n - 1);
}
int main() {
    constexpr int x = factorial(5);  // OK: 120
    
    int n = 10;
    // int y = factorial(n);  // Error: n은 런타임 값
}
flowchart TD
    subgraph constexpr[constexpr]
        ce1["컴파일 타임 OK"]
        ce2["런타임 OK"]
    end
    subgraph consteval[consteval]
        cv1["컴파일 타임 OK"]
        cv2["런타임 Error"]
    end

constexpr 함수의 “모호함”은 구체적으로 이런 뜻입니다. constexpr은 “컴파일 타임에 평가될 수도 있다”는 허가이지 명령이 아닙니다. 결과를 constexpr 변수나 배열 크기, 템플릿 인자처럼 상수가 필요한 자리에 쓰면 컴파일 타임에 평가되지만, 일반 변수에 대입하면 컴파일러는 런타임 호출로 둘 수 있습니다. 최적화 과정에서 상수로 접히는 경우가 많긴 해도 보장은 없습니다. 그래서 “이 해시 값은 반드시 빌드 시점에 계산돼야 한다”거나 “이 검증은 런타임에 실행되면 의미가 없다”는 요구가 있을 때 constexpr만으로는 부족합니다.

consteval은 이 요구를 언어 차원에서 강제합니다. 모든 호출이 상수 표현식이어야 하므로, 런타임 값이 섞이면 컴파일이 실패합니다. 결과적으로 consteval 함수는 바이너리에 코드가 남지 않고(함수 주소를 취할 수도 없습니다), 호출 자리는 계산된 값으로 대체됩니다. 이 덕분에 잘못된 입력을 “런타임에 실패하는 코드”가 아니라 “컴파일되지 않는 코드”로 만들 수 있다는 것이 consteval의 진짜 가치입니다. 성능 최적화보다는 검증 도구에 가깝다고 보면 이해가 쉽습니다.


constexpr과 consteval의 차이

항목별 비교

항목constexprconsteval
컴파일 타임 실행가능필수
런타임 실행가능불가
용도유연한 계산컴파일 타임 보장
에러런타임 값 OK런타임 값 Error

같은 함수를 두 방식으로 선언해 보기

// constexpr: 둘 다 가능
constexpr int add(int a, int b) {
    return a + b;
}
int main() {
    constexpr int x = add(1, 2);  // 컴파일 타임
    
    int a = 5;
    int y = add(a, 10);  // 런타임 (OK)
}
// consteval: 컴파일 타임만
consteval int multiply(int a, int b) {
    return a * b;
}
int main() {
    constexpr int x = multiply(2, 3);  // OK
    
    int a = 5;
    // int y = multiply(a, 10);  // Error: a는 런타임 값
}

즉시 함수(immediate function)의 평가 규칙

consteval 함수는 즉시 함수라고 하며, 호출 시점에 즉시 평가되어야 합니다.

consteval int square(int x) {
    return x * x;
}
// consteval 함수 안에서는 매개변수를 그대로 넘겨도 됨
consteval int sum_of_squares(int a, int b) {
    return square(a) + square(b);  // OK
}
// constexpr 함수에서 "매개변수"를 넘기면 불가 (C++20 기준)
constexpr int wrapper(int x) {
    // return square(x);  // Error: x는 상수 표현식이 아님
    return square(3) + x;  // OK: square(3)은 그 자체로 상수 표현식
}
int main() {
    constexpr int result = sum_of_squares(3, 4);  // 25
}

규칙을 정확히 말하면 “consteval 함수는 consteval 안에서만 호출할 수 있다”가 아니라 “consteval 함수의 호출은 그 자체로 상수 표현식이어야 한다”입니다. 그래서 main() 같은 일반 함수에서도 square(5)는 호출할 수 있고, constexpr 함수 안에서도 square(3)처럼 인자가 상수라면 문제없습니다. 안 되는 것은 constexpr 함수의 매개변수를 넘기는 경우입니다. constexpr 함수는 런타임에도 호출될 수 있으므로 그 매개변수 x는 상수라는 보장이 없기 때문입니다. 반면 consteval 함수 안의 매개변수는 그 함수 자체가 항상 컴파일 타임에 실행되므로 다른 즉시 함수에 자유롭게 넘길 수 있습니다.

C++23(P2564)에서는 이 규칙이 한 번 더 완화되어, 매개변수를 즉시 함수에 넘기는 constexpr 함수는 컴파일러가 자동으로 즉시 함수로 승격시킵니다. 즉 위의 주석 처리된 return square(x);가 C++23 모드의 최신 컴파일러에서는 컴파일되고, 대신 wrapper 자체를 런타임 값으로 호출할 수 없게 됩니다. 컴파일러와 -std 옵션에 따라 결과가 달라지는 지점이니 알아 두세요.


범위·해시·타입 크기를 컴파일 타임에 검증하기

값 범위 검증

consteval int check_range(int value, int min, int max) {
    if (value < min || value > max) {
        throw "Value out of range";
    }
    return value;
}
int main() {
    constexpr int size = check_range(100, 1, 1024);  // OK
    int buffer[size];
    
    // constexpr int bad = check_range(2000, 1, 1024);  // Compile error
}

throw를 이렇게 쓰는 것은 컴파일 타임 검증의 표준적인 관용구입니다. consteval 함수 안에 throw가 있는 것은 허용되지만, 상수 평가 중에 실제로 그 경로를 지나가면 상수 표현식이 아니게 되어 컴파일 에러가 납니다. 그래서 정상 값은 통과하고 범위를 벗어난 값만 빌드를 멈춥니다. GCC는 “error: expression '' is not a constant expression”처럼 throw가 있는 줄을 가리켜 주고, 던지는 문자열 리터럴은 실제로 던져지지 않지만 에러 메시지 근처에 나타나 원인을 알려 주는 역할을 합니다. static_assert와 비교하면 함수 인자에 따라 검증할 수 있다는 점이 다릅니다. static_assert는 템플릿 인자나 상수에만 쓸 수 있고 함수 매개변수는 검사할 수 없습니다.

문자열 해시

#include <iostream>
#include <string>
#include <string_view>
// 런타임 입력(switch 조건)에도 써야 하므로 consteval이 아니라 constexpr
constexpr unsigned int hash(std::string_view str) {
    unsigned int hash = 5381;
    for (char c : str) {
        hash = ((hash << 5) + hash) + static_cast<unsigned char>(c);
    }
    return hash;
}
enum class MessageType : unsigned int {
    Login = hash("login"),
    Logout = hash("logout"),
    Data = hash("data")
};
void handle_message(const std::string& type) {
    switch (hash(type)) {  // 런타임 해시
    case hash("login"):    // case 라벨은 컴파일 타임 해시
        std::cout << "Login\n";
        break;
    case hash("logout"):
        std::cout << "Logout\n";
        break;
    case hash("data"):
        std::cout << "Data\n";
        break;
    }
}

이 예제는 consteval의 한계를 보여 주는 좋은 사례입니다. switch의 조건 hash(type)은 실행 중에 들어온 문자열이라 런타임에 계산할 수밖에 없습니다. hash를 consteval로 선언하면 이 줄에서 “call to consteval function ‘hash’ is not a constant expression” 에러가 납니다. 같은 함수를 컴파일 타임(case 라벨, enum 값)과 런타임(조건식) 양쪽에서 써야 하므로 여기서는 constexpr이 맞습니다. case 라벨은 상수 표현식이 필요한 자리라 constexpr 함수여도 반드시 컴파일 타임에 계산됩니다.

실무에서 이 패턴을 쓸 때는 해시 충돌을 염두에 둬야 합니다. 서로 다른 두 case 문자열이 같은 해시를 내면 컴파일러가 “duplicate case value” 에러로 알려 주므로 case끼리의 충돌은 안전하게 잡힙니다. 문제는 case에 없는 입력 문자열이 우연히 어떤 case와 같은 해시를 내는 경우로, 이때는 엉뚱한 분기로 들어갑니다. 보안이나 정확성이 중요한 분기라면 해시로 분기한 뒤 type == "login"처럼 문자열을 한 번 더 비교해야 합니다. 이 해시는 djb2 알고리즘이며, 32비트 부호 없는 정수라 오버플로는 정의된 동작(모듈러 연산)입니다.

타입 크기 검증

template<typename T>
consteval bool is_small_type() {
    return sizeof(T) <= 16;
}
template<typename T>
    requires is_small_type<T>()
void process(T value) {
    // T는 16바이트 이하 보장
}
int main() {
    process(42);        // OK: sizeof(int) = 4
    process(3.14);      // OK: sizeof(double) = 8
    // process(std::string{"hello"});  // Error: sizeof(string) > 16
}

requires 절에는 컴파일 타임에 bool로 평가되는 식이 필요하므로 consteval 함수가 잘 맞습니다. 다만 이 정도 조건이라면 template<typename T> concept SmallType = sizeof(T) <= 16;처럼 concept으로 만드는 편이 더 관용적이고, 제약을 어겼을 때의 에러 메시지도 “constraints not satisfied … SmallTypestd::string”처럼 더 읽기 쉽습니다. sizeof(std::string)은 구현마다 달라서(libstdc++·MSVC 64비트는 32바이트, libc++는 24바이트) 이런 크기 기준 제약은 플랫폼에 따라 결과가 바뀔 수 있다는 점도 기억해 두세요.


팩토리얼·문자열·설정값 계산

컴파일 타임 팩토리얼

consteval int factorial(int n) {
    if (n < 0) throw "Negative factorial";
    return n <= 1 ? 1 : n * factorial(n - 1);
}
int main() {
    constexpr int f5 = factorial(5);   // 120
    constexpr int f10 = factorial(10); // 3628800
    
    // 배열 크기로 사용
    int buffer[factorial(4)];  // 24 elements
}

factorial(13)부터는 int 범위를 넘습니다. 런타임이라면 부호 있는 정수 오버플로는 조용히 쓰레기 값을 내는 미정의 동작이지만, 상수 평가 중의 미정의 동작은 컴파일 에러가 됩니다. constexpr int f13 = factorial(13);을 쓰면 “overflow in constant expression” 같은 에러로 빌드가 멈춥니다. 컴파일 타임 계산은 이처럼 런타임에 놓치기 쉬운 오버플로, 배열 범위 초과, 널 포인터 역참조를 컴파일러가 대신 잡아 준다는 부수 효과가 있습니다. 재귀 깊이에는 컴파일러 한도가 있어서(GCC 기본 -fconstexpr-depth=512) 깊은 재귀는 반복문으로 바꾸는 편이 안전합니다.

컴파일 타임 문자열 처리

#include <array>
#include <string_view>
consteval std::size_t count_chars(std::string_view str, char c) {
    std::size_t count = 0;
    for (char ch : str) {
        if (ch == c) ++count;
    }
    return count;
}
int main() {
    constexpr auto count = count_chars("hello world", 'l');
    static_assert(count == 3);
    
    std::array<char, count> buffer;  // 3 elements
}

컴파일 타임 설정

#include <array>
#include <thread>
consteval int get_buffer_size() {
    #ifdef LARGE_BUFFER
        return 1024 * 1024;  // 1MB
    #else
        return 4096;         // 4KB
    #endif
}
consteval int get_thread_count() {
    return 8;  // 컴파일 타임 상수
}
int main() {
    constexpr int buffer_size = get_buffer_size();
    constexpr int threads = get_thread_count();
    
    char buffer[buffer_size];
    std::array<std::thread, threads> thread_pool;
}

이 예제에서 consteval이 주는 이점은 크지 않다는 점을 솔직히 짚어 두겠습니다. 상수를 반환하기만 하는 함수라면 constexpr int buffer_size = ...; 변수 하나로 충분하고, #ifdef 분기도 변수 정의에 그대로 쓸 수 있습니다. 함수로 만드는 의미가 생기는 것은 값을 계산하거나 검증할 때입니다. 또 LARGE_BUFFER가 정의되면 1MB짜리 char 배열이 스택에 잡히는데, Windows의 기본 스택 크기가 1MB라 이 한 줄로 스택 오버플로가 납니다. 컴파일 타임 상수라고 해서 큰 버퍼를 지역 배열로 만들어도 된다는 뜻은 아니므로, 큰 버퍼는 static이나 힙에 두어야 합니다.


consteval 호출이 거부되는 경우

런타임 값을 인자로 넘김

증상: error: call to consteval function is not a constant expression.

consteval int square(int x) {
    return x * x;
}
int main() {
    int a = 5;
    // int b = square(a);  // Error: a는 런타임 값
    
    // ✅ 해결법 1: constexpr 변수
    constexpr int c = 5;
    int d = square(c);  // OK
    
    // ✅ 해결법 2: 리터럴
    int e = square(5);  // OK
}

int a = 5;처럼 초기값이 리터럴이어도 a는 const나 constexpr이 아니므로 상수 표현식이 아닙니다. 사람 눈에는 값이 뻔해 보여도 컴파일러는 “변수 a의 값은 실행 중에 바뀔 수 있다”고 보기 때문입니다. const int a = 5;로 바꾸면 정수 타입에 한해 상수 표현식으로 취급되어 통과합니다. Clang은 “call to consteval function ‘square’ is not a constant expression” 아래에 “read of non-const variable ‘a’ is not allowed in a constant expression”이라는 보충 메시지를 붙여 원인을 정확히 짚어 줍니다. 런타임 값으로도 호출해야 하는 함수라면 그 함수는 애초에 consteval이 아니라 constexpr이어야 한다는 신호입니다.

함수 안에서 부작용을 일으킴

증상: error: call to non-constexpr function. 원인: consteval 함수 안에서 I/O처럼 constexpr이 아닌 함수를 호출하거나, 전역 변수를 수정하는 등 상수 평가에서 허용되지 않는 연산을 수행. (C++20부터 new/delete와 std::vector, std::string 같은 동적 할당은 평가가 끝나기 전에 해제된다면 상수 평가 안에서도 허용됩니다. 할당한 메모리를 결과로 밖에 내보낼 수는 없습니다.)

// ❌ 잘못된 사용
consteval int bad() {
    std::cout << "Hello\n";  // Error: I/O
    return 42;
}
// ✅ 올바른 사용: 순수 계산만
consteval int good(int x) {
    return x * 2;
}

constexpr 함수의 매개변수를 consteval에 넘김

원인: constexpr 함수는 런타임에도 실행될 수 있으므로, 그 매개변수를 consteval 함수에 넘길 수 없습니다(C++20 기준).

#include <type_traits>
consteval int immediate(int x) {
    return x * 42;
}
// ❌ 잘못된 사용
constexpr int wrapper(int x) {
    return immediate(x);  // Error: x는 상수 표현식이 아님
}
// ✅ 올바른 사용: consteval에서 호출
consteval int wrapper2(int x) {
    return immediate(x);  // OK
}
// ✅ 또는 컴파일 타임/런타임 경로를 나누기
constexpr int wrapper3(int x) {
    if (std::is_constant_evaluated()) {
        return x * 42;  // 컴파일 타임 경로
    } else {
        return x * 2;   // 런타임 경로
    }
}

wrapper3의 std::is_constant_evaluated()는 “지금 상수 평가 중인가”를 알려 주지만, 이 if 안에서도 immediate(x)를 호출할 수는 없습니다. 이 함수는 일반 bool을 반환하는 함수라 컴파일러 입장에서는 두 분기 모두 런타임에 실행될 수 있는 코드이기 때문입니다. 흔한 실수로 if constexpr (std::is_constant_evaluated())라고 쓰면 조건이 항상 참이 되어(if constexpr의 조건은 그 자체가 상수 평가이므로) 런타임 분기가 사라집니다. GCC와 Clang은 이 경우 “always evaluates to true” 경고를 냅니다.

C++23의 if consteval { ... } else { ... }은 이 문제를 해결합니다. if consteval 블록 안은 즉시 함수 문맥으로 취급되어 immediate(x)를 호출할 수 있고, 런타임 분기와 깔끔하게 나눌 수 있습니다. C++23을 쓸 수 있다면 is_constant_evaluated() 대신 이쪽을 권합니다.


문자열 검증·룩업 테이블·설정 계산 패턴

식별자 문자열 컴파일 타임 검증

#include <string_view>
// std::isalpha/isalnum은 constexpr이 아니므로 직접 구현
constexpr bool is_alpha(char c) {
    return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}
constexpr bool is_alnum(char c) {
    return is_alpha(c) || (c >= '0' && c <= '9');
}
consteval bool is_valid_identifier(std::string_view str) {
    if (str.empty()) return false;
    if (!is_alpha(str[0]) && str[0] != '_') return false;
    
    for (char c : str) {
        if (!is_alnum(c) && c != '_') return false;
    }
    return true;
}
template<std::size_t N>
consteval auto make_identifier(const char (&str)[N]) {
    if (!is_valid_identifier(str)) {
        throw "Invalid identifier";
    }
    return std::string_view(str, N - 1);
}
int main() {
    constexpr auto id1 = make_identifier("valid_name");  // OK
    // constexpr auto id2 = make_identifier("123invalid");  // Compile error
}

처음 이 패턴을 작성할 때 흔히 막히는 곳이 <cctype>의 std::isalpha, std::isalnum입니다. 이 함수들은 로케일에 의존하는 C 라이브러리 함수라 constexpr이 아니고, consteval 함수 안에서 호출하면 “call to non-‘constexpr’ function ‘int isalpha(int)’” 에러가 납니다. 그래서 위처럼 ASCII 범위를 직접 비교하는 작은 함수를 만들어 씁니다. 이 패턴은 로그 태그, 설정 키, 직렬화 필드 이름처럼 소스 코드에 문자열 리터럴로 적는 식별자의 오타나 규칙 위반을 빌드 단계에서 막는 데 유용합니다. fmt/std::format이 형식 문자열의 {} 개수와 인자 개수를 컴파일 타임에 검사하는 것도 같은 원리로, 내부적으로 consteval 생성자를 씁니다.

컴파일 타임 룩업 테이블

#include <array>
#include <cstdint>
consteval std::array<std::uint32_t, 256> generate_crc_table() {
    std::array<std::uint32_t, 256> table{};
    for (std::uint32_t i = 0; i < 256; ++i) {
        std::uint32_t crc = i;
        for (int j = 0; j < 8; ++j) {
            crc = (crc >> 1) ^ ((crc & 1) ? 0xEDB88320u : 0u);
        }
        table[i] = crc;
    }
    return table;
}
constexpr auto CRC_TABLE = generate_crc_table();
std::uint32_t crc32(const char* data, std::size_t length) {
    std::uint32_t crc = 0xFFFFFFFF;
    for (std::size_t i = 0; i < length; ++i) {
        crc = (crc >> 8) ^ CRC_TABLE[(crc ^ static_cast<unsigned char>(data[i])) & 0xFF];
    }
    return ~crc;
}

CRC 테이블은 타입 선택이 결과를 좌우합니다. 테이블과 중간값을 int로 두면 다항식 0xEDB88320과 XOR한 결과의 최상위 비트가 1이 되는 순간 값이 음수가 되고, 음수에 대한 >>는 부호 비트를 채우는 산술 시프트라 이후 계산이 모두 틀어집니다. 에러도 경고도 없이 틀린 CRC가 나오기 때문에 표준 테스트 벡터("123456789"의 CRC-32는 0xCBF43926)로 검증해 보기 전에는 알기 어렵습니다. 비트 연산은 항상 std::uint32_t 같은 부호 없는 고정 폭 타입으로 하세요. data[i]를 unsigned char로 변환한 것도 같은 이유로, char가 부호 있는 플랫폼에서 0x80 이상의 바이트가 음수로 확장되는 것을 막습니다.

테이블을 constexpr 변수로 만들었기 때문에 이 256개 값은 바이너리의 읽기 전용 데이터 영역에 미리 계산된 채로 들어갑니다. 프로그램 시작 시 테이블을 채우는 초기화 코드가 없고, 여러 스레드가 동시에 읽어도 초기화 경쟁이 없습니다. 그렇다고 컴파일 타임 계산이 항상 이득은 아니어서, 수만 개 항목의 테이블을 상수 평가로 만들면 컴파일 시간이 눈에 띄게 늘고 컴파일러의 상수 평가 단계 한도(GCC -fconstexpr-ops-limit, Clang -fconstexpr-steps)에 걸려 빌드가 실패할 수 있습니다.

빌드 설정값 계산

consteval int calculate_pool_size() {
    #ifdef PRODUCTION
        return 128;
    #elif defined(STAGING)
        return 64;
    #else
        return 16;
    #endif
}
consteval int calculate_timeout_ms() {
    return 30 * 1000;  // 30초
}
int main() {
    constexpr int pool_size = calculate_pool_size();
    constexpr int timeout = calculate_timeout_ms();
    
    static_assert(pool_size > 0);
    static_assert(timeout > 0);
}

컴파일 타임 설정 시스템 만들기

#include <string_view>
#include <array>
// 설정 키-값 쌍
struct ConfigEntry {
    std::string_view key;
    int value;
};
// 컴파일 타임 설정 생성
consteval auto generate_config() {
    std::array<ConfigEntry, 3> config{{
        {"max_connections", 1000},
        {"timeout_ms", 30000},
        {"buffer_size", 4096}
    }};
    return config;
}
// 컴파일 타임 설정 조회
consteval int get_config(std::string_view key) {
    constexpr auto config = generate_config();
    for (const auto& entry : config) {
        if (entry.key == key) {
            return entry.value;
        }
    }
    throw "Config key not found";
}
int main() {
    constexpr int max_conn = get_config("max_connections");  // 1000
    constexpr int timeout = get_config("timeout_ms");        // 30000
    
    std::array<int, max_conn> connection_pool;
    
    static_assert(max_conn == 1000);
    static_assert(timeout == 30000);
}

이 설정 시스템의 장점은 get_config("max_conection")처럼 키에 오타가 나면 throw에 도달해 컴파일이 실패한다는 것입니다. 문자열 키로 설정을 찾는 코드는 보통 런타임에 “키 없음”으로 실패하는데, 여기서는 빌드 단계에서 잡힙니다. 조회가 선형 탐색이지만 전부 컴파일 타임에 끝나므로 런타임 비용은 0입니다. 한계도 분명합니다. 값이 바이너리에 박히므로 설정을 바꾸려면 다시 빌드해야 하고, 운영 중에 바꿔야 하는 값(타임아웃 튜닝, 기능 플래그)에는 맞지 않습니다. 빌드 변형마다 고정되는 값(버퍼 크기, 최대 연결 수의 상한)에 한정해 쓰는 편이 좋습니다.


consteval 핵심 정리

개념설명
consteval컴파일 타임 전용 함수
즉시 함수호출 시점에 즉시 평가
constexpr 차이consteval은 런타임 실행 불가
용도컴파일 타임 검증, 메타프로그래밍

consteval은 컴파일 타임 계산을 강제해, 런타임 오버헤드 없이 복잡한 계산을 수행할 수 있습니다.


FAQ

Q1: consteval vs constexpr 언제 쓰나요?

A: 컴파일 타임 계산을 강제하고 싶으면 consteval, 유연하게 둘 다 허용하려면 constexpr을 사용하세요.

Q2: consteval 함수를 constexpr에서 호출할 수 있나요?

A: 호출 자체가 상수 표현식이면 가능합니다. constexpr 함수 안에서 immediate(3)처럼 상수 인자로 부르는 것은 괜찮지만, constexpr 함수의 매개변수를 넘기는 것은 C++20에서 에러입니다. C++23에서는 그런 constexpr 함수가 자동으로 즉시 함수로 승격되고, if consteval 블록 안에서도 호출할 수 있습니다.

Q3: 런타임 값을 전달하면?

A: 컴파일 에러가 납니다. consteval 함수는 모든 인자가 컴파일 타임 상수여야 합니다.

Q4: 부작용이 있으면?

A: I/O, 전역 변수 수정, constexpr이 아닌 함수 호출은 사용할 수 없습니다. C++20부터는 평가 안에서 할당하고 해제까지 끝나는 동적 할당(std::vector, std::string 포함)은 허용되지만, 할당한 메모리를 결과로 밖에 내보낼 수는 없습니다.

Q5: 컴파일러 지원은?

A:

  • GCC 10+: 지원
  • Clang: 초기 버전(1114)은 부분 지원이었고, 여러 버그가 수정된 Clang 1517 무렵부터 안정적
  • MSVC: VS 2019 16.10(19.29)+
  • C++23의 if consteval과 즉시 함수 승격(P2564)은 더 최신 컴파일러가 필요하므로 사용하는 버전의 지원표를 확인하세요.

Q6: consteval 학습 리소스는?

A:


같이 보면 좋은 글