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
더 알아보기
- constexpr 지정자 (C++11) — 변수나 함수의 값을 컴파일 타임에 계산할 수 있음을 지정해요.
- constinit 지정자 (C++20) — 변수가 정적 초기화, 즉 zero 초기화와 상수 초기화를 가짐을 단언해요.
- constant expression — 컴파일 타임에 평가할 수 있는 표현식을 정의해요.