consteval 지정자

consteval 지정자 (consteval specifier)

constexpr로 함수를 만들었는데, 그 함수가 반드시 컴파일 타임에만 평가되길 원할 때가 있어요. consteval 지정자가 바로 그걸 보장해줘요. 함수에 consteval을 붙이면 그 함수는 immediate 함수가 되어, 모든 호출이 컴파일 타임 상수여야 해요. 이 페이지에서 그 규칙을 자세히 살펴볼게요.

출처: cppreference

본문

consteval 지정자는 함수나 함수 템플릿을 immediate 함수로 선언해요. 즉, 그 함수에 대한 평가 가능한(every potentially-evaluated) 모든 호출이 (직간접적으로) 컴파일 타임 상수 표현식을 만들어야 해요.

immediate 함수는 constexpr 함수이기도 해요. 상황에 따라 그 요구사항을 따라요. constexpr과 마찬가지로 consteval 지정자는 inline을 암시해요. 다만 소멸자, 할당 함수, 해제 함수에는 적용할 수 없어요.

consteval을 지정한 함수나 함수 템플릿 선언은 constexpr도 함께 지정할 수 없고, 그 함수나 함수 템플릿의 모든 재선언도 반드시 consteval을 지정해야 해요.

가장 안쪽 비-블록 스코프가 immediate 함수의 함수 매개변수 스코프도, consteval if 문의 참 분기(since C++23)도 아닌 곳에서 immediate 함수를 평가 가능하게 호출하면 반드시 상수 표현식을 만들어야 해요. 그런 호출을 immediate 호출(immediate invocation) 이라고 불러요.

이 섹션은 불완전해요. 사유: immediate-escalating 함수를 반영하도록 갱신 필요.

consteval int sqr(int n)
{
    return n*n;
}
constexpr int r = sqr(100); // OK

int x = 100;
int r2 = sqr(x);            // Error: Call does not produce a constant

consteval int sqrsqr(int n)
{
    return sqr(sqr(n));     // Not a constant expression at this point, but OK
}

constexpr int dblsqr(int n)
{
    return 2 * sqr(n);      // Error: Enclosing function is not consteval
                            // and sqr(n) is not a constant
}

immediate 함수는 immediate 호출의 부분 표현식 안에서나 immediate 함수 문맥(위에서 말한, immediate 함수 호출이 상수 표현식일 필요가 없는 문맥) 안에서만 이름 붙일 수 있어요. immediate 함수에 대한 포인터나 참조는 취할 수 있지만 상수 표현식 평가를 벗어날 수는 없어요.

consteval int f() { return 42; }
consteval auto g() { return &f; }
consteval int h(int (*p)() = g()) { return p(); }
constexpr int r = h();  // OK
constexpr auto e = g(); // ill-formed: a pointer to an immediate function is
                        // not a permitted result of a constant expression

참고 (Notes)

피처 테스트 매크로 (Feature-test macro)

매크로 표준 피처
__cpp_consteval 201811L (C++20) immediate 함수
__cpp_consteval 202211L (C++23) (DR20) consteval의 전파(consteval propagate up)

키워드 (Keywords)

consteval

예제 (Example)

#include <iostream>

// This function might be evaluated at compile-time, if the input
// is known at compile-time. Otherwise, it is executed at run-time.
constexpr unsigned factorial(unsigned n)
{
    return n < 2 ? 1 : n * factorial(n - 1);
}

// With consteval we enforce that the function will be evaluated at compile-time.
consteval unsigned combination(unsigned m, unsigned n)
{
    return factorial(n) / factorial(m) / factorial(n - m);
}

static_assert(factorial(6) == 720);
static_assert(combination(4, 8) == 70);

int main(int argc, const char*[])
{
    constexpr unsigned x{factorial(4)};
    std::cout << x << '\n';
    
    [[maybe_unused]]
    unsigned y = factorial(argc); // OK
//  unsigned z = combination(argc, 7); // error: 'argc' is not a constant expression
}

출력:

24

더 알아보기