본문 바로가기
개발/C++ (98,03,11,14,17,20,23)

Modern C++ : const vs constexpr (98, 11, 14, 17, 20)

by snowoods 2025. 8. 6.

Modern C++

상수와 상수 표현식 : const vs constexpr

 

개요

C++에서 constconstexpr는 모두 상수를 정의하는 데 사용되지만, 초기화 시점과 컴파일 타임 평가 여부에서 중요한 차이가 있습니다. const는 런타임에 초기화될 수 있는 읽기 전용 변수를 선언하는 반면, constexpr는 컴파일 타임에 값이 결정되는 진정한 의미의 상수를 선언하여 컴파일러 최적화와 타입 시스템에 더 많은 정보를 제공합니다.

 

C++ 버전별 주요 키워드 도입 시기

  • C++98: const 키워드를 사용하여 읽기 전용 변수를 선언했습니다.
  • C++11: constexpr 키워드가 도입되어, 변수나 함수가 컴파일 타임에 평가될 수 있음을 명시적으로 나타낼 수 있게 되었습니다.
  • C++14: constexpr 함수의 제약이 완화되어, 지역 변수 선언, 반복문, 조건문 등을 포함할 수 있게 되어 활용성이 크게 향상되었습니다.

 

내용 설명

const

const로 선언된 변수는 초기화된 이후 값을 변경할 수 없는 읽기 전용(read-only) 속성을 가집니다. 하지만 초기화 자체는 런타임에 수행될 수 있습니다.

  • 초기화: 런타임에 결정되는 값으로 초기화될 수 있습니다. (예: 함수 반환 값, 사용자 입력 등)
  • 사용: 주로 실행 중에 변하지 않아야 할 값을 보호하는 목적으로 사용됩니다.
int get_value() { return 10; }

const int val1 = 10; // 컴파일 타임 상수
const int val2 = get_value(); // 런타임에 초기화되는 상수

 

constexpr (Constant Expression)

constexpr컴파일 타임 상수(compile-time constant)를 선언하는 데 사용됩니다. constexpr로 선언된 변수나 함수의 결과는 반드시 컴파일 시점에 그 값을 결정할 수 있어야 합니다.

  • 초기화: 반드시 컴파일 타임에 알 수 있는 값으로 초기화되어야 합니다.
  • 사용: 컴파일 타임에 계산을 완료하여 프로그램의 런타임 성능을 향상시키거나, 템플릿 메타프로그래밍, 배열 크기 지정 등 컴파일 시점에 값이 필요한 곳에 사용됩니다.
  • constexpr 변수는 암시적으로 const 속성을 가집니다.

 

예제 코드

#include <iostream>
#include <cstdint>

// C++14 이상에서는 내부 로직이 더 복잡해질 수 있습니다.
constexpr std::uint64_t faculty(const std::uint8_t n)
{
    // C++11 constexpr 함수는 return 문 하나만 허용했지만,
    // C++14부터는 지역 변수, 루프, 조건문 등을 사용할 수 있습니다.
    auto result = std::uint64_t{1};

    for (std::uint8_t i = 1; i <= n; i++)
    {
        result *= i;
    }

    return result;
}

int main()
{
    // 1. const 변수: 런타임에 초기화될 수 있음
    std::cout << "런타임에 값을 입력하세요: ";
    int input;
    std::cin >> input;
    const int runtime_const = input; // OK: 런타임 값으로 초기화

    // 2. constexpr 변수: 컴파일 타임에 값이 결정되어야 함
    constexpr auto compile_time_val = faculty(5); // OK: 컴파일 타임에 계산됨
    // constexpr auto runtime_val = faculty(input); // ERROR: input은 컴파일 타임 상수가 아님

    // 3. 컴파일 타임 상수 활용
    int arr[compile_time_val]; // OK: 배열 크기는 컴파일 타임 상수가 필요함
    // int arr2[runtime_const]; // ERROR: C-style 배열 크기는 컴파일 타임 상수가 필요함 (단, 일부 컴파일러 확장 기능으로 허용될 수 있음)

    std::cout << "faculty(5) = " << compile_time_val << std::endl;
    std::cout << "런타임 상수 값: " << runtime_const << std::endl;
    std::cout << "배열 arr의 크기: " << sizeof(arr) / sizeof(int) << std::endl;

    return 0;
}

 

실행 결과

런타임에 값을 입력하세요: 10
faculty(5) = 120
런타임 상수 값: 10
배열 arr의 크기: 120

 

활용팁

  • 성능 최적화: 런타임에 수행될 계산을 컴파일 타임으로 옮겨 프로그램의 실행 속도를 높일 수 있습니다. 복잡한 수학 계산, 테이블 생성 등을 constexpr 함수로 구현하면 좋습니다.
  • 컴파일 타임 안정성: 배열의 크기, 템플릿 인자, static_assert 등 컴파일 시점에 값이 확정되어야 하는 곳에 constexpr를 사용하면 코드의 안정성과 명확성이 향상됩니다.
  • constconstexpr의 선택 기준:
    • 값이 컴파일 시점에 확정될 수 있다면 항상 constexpr를 우선적으로 사용하세요.
    • 값이 런타임에 결정되지만, 한 번 초기화된 후 변경되지 않아야 한다면 const를 사용하세요. (예: 설정 파일에서 읽어온 값)