C++ constexpr, 컴파일 타임에 계산하는 이유

constexpr은 변수나 함수의 값을 컴파일 타임에 평가할 수 있음을 선언하는 지정자입니다. 객체 선언에서는 const를 함의하고, 상수 식이 필요한 위치에 쓸 수 있지만, 인수가 상수가 아니면 같은 함수가 실행 시간에 호출될 수도 있습니다. 이 글은 cppreference의 상수 식 지정자와 상수 식 문서를 2026-08-27 기준으로 정리한 일반 설명이며, 표준 버전과 구현에 따라 세부 동작은 달라질 수 있습니다.

컴파일 타임에는 무엇을 계산하나?

한 줄 답: 상수 식은 컴파일 타임에 평가할 수 있는 식이며, 이 지정자가 붙은 변수나 함수의 값은 적절한 조건에서 상수 식이 필요한 위치에 사용할 수 있습니다.

상수 식은 평가 결과를 컴파일 타임에 얻을 수 있는 식을 가리키며, 비타입 템플릿 인자나 배열 크기처럼 상수 식만 허용되는 위치에 사용할 수 있습니다. 핵심은 모든 계산을 자동으로 미리 실행한다는 뜻이 아니라, 필요한 조건을 만족할 때 그 값을 상수 식으로 사용할 수 있다는 점입니다. 변수의 초기화와 함수 호출에 들어가는 인수가 상수 식 규칙을 만족하는지 함께 확인해야 합니다.

int n = 1;
std::array<int, n> a1;  // Error: “n” is not a constant expression
const int cn = 2;
std::array<int, cn> a2; // OK: “cn” is a constant expression

n은 일반 정수 객체이므로 std::array의 크기 인자로 사용할 상수 식이 아닙니다. cn은 상수 초기화된 const 정수형 객체이므로 같은 위치의 크기 인자로 사용할 수 있습니다. 이 예시는 계산 결과 자체보다 상수 식이 필요한 문맥과 객체의 초기화 상태를 구분하는 데 의미가 있습니다.

const와는 무엇이 다른가?

한 줄 답: const는 그 이름을 통해 객체를 수정하지 못하게 하는 한정자이고, 이 지정자가 붙은 객체 선언은 const를 함의하면서 상수 식 초기화와 리터럴 타입 같은 추가 조건을 요구합니다.

const는 그 이름을 통해 객체를 수정하지 못하게 하지만, 초기화 식이 반드시 컴파일 타임 상수일 필요는 없습니다. 반면 이 지정자가 붙은 변수는 정의여야 하고 선언에서 초기화해야 하며, 리터럴 타입이어야 합니다. 또한 초기화의 전체 식이 상수 식이어야 하므로 const보다 요구 조건이 좁습니다.

const std::size_t tabsize = 50;
int tab[tabsize]; // OK: tabsize is a constant expression
                  // because it has const-qualified integral type, and
                  // its initializer is a constant initializer

std::size_t n = 50;
const std::size_t sz = n;
int tab2[sz]; // Error: sz is not a constant expression
              // because its initializer was not a constant initializer

tabsizeconst 정수형이며 초기화 값 50이 상수 초기화이므로 배열 경계에 사용할 수 있습니다. szconst이지만 실행 시간 값일 수 있는 n으로 초기화되므로 배열 경계에 사용할 상수 식이 아닙니다. 따라서 이 지정자가 붙은 모든 변수는 const이지만, 모든 const 변수가 컴파일 타임 상수인 것은 아닙니다.

함수와 변수에서는 어떻게 쓰나?

한 줄 답: 변수는 정의·초기화·리터럴 타입 등의 조건을 충족해야 하고, 이 함수는 적절한 인수라면 상수 식에서 호출할 수 있으며 C++14부터는 루프를 담을 수 있습니다.

변수 선언은 단순히 이름만 알리는 선언이 아니라 정의여야 하며, 선언에서 초기화해야 합니다. 타입은 리터럴 타입이어야 하며, 리터럴 타입은 상수 식 안에서 생성·조작·반환할 수 있는 타입 범주입니다. 스칼라 타입, 참조 타입, 리터럴 타입의 배열, 조건을 만족하는 클래스 타입 등이 이 범주에 포함될 수 있습니다.

constexpr int factorial(int n)
{
    return n <= 1 ? 1 : (n * factorial(n - 1));
}

C++11의 재귀 예시는 단일 반환문으로 계산을 표현한 형태입니다. C++14부터는 지역 변수와 반복문을 사용할 수 있으므로 같은 계산을 다음처럼 작성할 수 있습니다.

constexpr int factorial_cxx14(int n)
{
    int res = 1;
    while (n > 1)
        res *= n--;
    return res;
}

두 함수 모두 인수가 상수 식으로 평가될 수 있으면 상수 식 문맥에서 호출할 수 있습니다. 인수가 실행 시간 값이면 같은 함수가 실행 시간에 호출될 수 있으므로, 선언만 보고 모든 호출이 컴파일 타임에 이뤄진다고 판단하면 안 됩니다. 함수 또는 함수 템플릿의 어느 선언에 이 지정자가 있으면 모든 선언에도 같은 지정자가 있어야 합니다. C++17부터 함수나 정적 데이터 멤버의 첫 선언에 쓰인 이 지정자는 inline을 함의합니다.

한계는 무엇인가?

한 줄 답: 이 함수 호출이 상수 식이 되려면 호출 경로와 연산이 상수 식 규칙을 지켜야 하며, 인수가 실행 시간 값이면 호출은 실행 시간에 이뤄질 수 있습니다.

상수 식 평가에서는 상수 식으로 호출할 수 없는 함수 호출, 정의되지 않은 동작, reinterpret_cast, 구현 정의 한도를 넘는 평가 등이 제한됩니다. 따라서 호출 경로에 포함된 함수와 모든 연산이 상수 식 규칙을 만족해야 하며, 하나라도 조건을 벗어나면 해당 문맥에서 컴파일 타임 값으로 사용할 수 없습니다.

constexpr int n = std::numeric_limits<int>::max(); // OK: max() is constexpr
constexpr int m = std::time(nullptr); // Error: std::time() is not constexpr

std::numeric_limits<int>::max()는 상수 식에서 호출할 수 있지만, std::time(nullptr)는 상수 식 초기화에 사용할 수 없습니다. 0으로 나누기나 정수 오버플로처럼 정의되지 않은 동작도 상수 식 평가에서 허용되지 않습니다.

C++20의 new는 평가 중에 할당한 저장 공간을 해제하는 경우 등을 제외하면 상수 식 평가에서 제한됩니다. 이처럼 이 지정자는 컴파일 타임 평가의 가능성을 열지만, 모든 호출이나 모든 연산을 그 시점으로 강제하지는 않습니다.

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은 C++20의 즉시 함수 지정자이며, 잠재적으로 평가되는 모든 호출이 컴파일 타임 상수를 만들어야 합니다. 따라서 sqr(100)은 가능하지만 sqr(x)는 오류가 됩니다. 이 함수는 조건을 만족하는 인수로 상수 식에서 호출할 수 있고 다른 인수로 실행 시간에도 호출할 수 있지만, 즉시 함수는 호출마다 컴파일 타임 상수를 요구합니다.

FAQ

한 줄 답: 상수 식이 필요한 자리인지, 초기화와 인수가 상수 식인지, 타입과 호출 경로가 조건을 만족하는지를 차례로 확인하면 됩니다.

질문답변
함수에 이 지정자를 사용하면 항상 컴파일 타임에 평가됩니까?아닙니다. 인수가 상수가 아니면 같은 함수가 실행 시간에 호출될 수 있습니다.
const이면 배열 크기에 항상 쓸 수 있습니까?아닙니다. const 객체도 초기화가 상수 식인지 확인해야 합니다. tabsize처럼 상수 초기화된 경우에는 가능하지만, 실행 시간 값으로 초기화한 sz는 상수 식이 아닙니다.
언제 consteval을 고려합니까?모든 호출이 컴파일 타임 상수가 되어야 할 때 고려합니다. 이 지정자는 조건이 맞으면 컴파일 타임 평가가 가능하다는 점에서 구분됩니다.

관련 개념을 면접 질문 형식으로 점검하려면 C++ 기술면접 질문 10가지를 참고할 수 있습니다.

출처

한 줄 답: 본문 사실과 코드 예시는 2026-08-27 기준의 cppreference 네 문서에서만 확인합니다.

  • constexpr specifier — 지정자의 적용 대상과 변수·함수 조건, C++11·C++14 예시를 확인할 수 있습니다.
  • Constant expressions — 상수 식의 쓰임, const 초기화 대비, 제한 사례를 확인할 수 있습니다.
  • LiteralType named requirement — 리터럴 타입의 역할과 조건을 확인할 수 있습니다.
  • consteval specifier — 즉시 함수와 호출 강제의 차이를 확인할 수 있습니다.