[C++20 새기능 소개] constexpr의 컨테이너 (std::vector, std::string) 지원

C++20에서는 constexpr 지원이 표준 라이브러리 전반에 걸쳐 확대되어, 이제는 std::vector, std::string 등의 컨테이너를 컴파일 타임 상수 표현식으로 사용할 수 있게 되었습니다. 이번 글에서는 constexpr 확장의 개념과 사용법, 그리고 이전 버전과 비교하여 어떻게 개선되었는지 알아보겠습니다.

constexpr 확장이란 무엇인가요?

constexpr 키워드는 변수나 함수, 그리고 객체가 컴파일 타임 상수 표현식으로 평가될 수 있음을 나타냅니다. C++20에서는 표준 라이브러리의 여러 컨테이너와 알고리즘에 constexpr이 적용되어, 컴파일 타임에 복잡한 데이터 구조를 생성하고 조작할 수 있게 되었습니다.

이전 버전에서는 어떻게 했나요?

C++11부터 constexpr이 도입되었지만, 표준 라이브러리의 컨테이너들은 constexpr을 지원하지 않았습니다. 따라서 컴파일 타임에 복잡한 데이터를 처리하려면 제한이 있었습니다.

예제: 이전 방식으로 컴파일 타임 상수 처리

constexpr int square(int x) {
    return x * x;
}

int main() {
    constexpr int value = square(5); // 올바름
    // std::vector<int> vec = {1, 2, 3}; // 오류: 컴파일 타임에 vector 생성 불가
    return 0;
}
  • 문제점:
    • std::vector와 같은 동적 크기 컨테이너는 constexpr로 사용할 수 없었습니다.
    • 컴파일 타임에 복잡한 데이터 구조를 생성하려면 사용자 정의 타입을 만들어야 했습니다.

C++20의 constexpr 확장을 사용한 개선

C++20에서는 std::vector, std::string, std::array, std::optional 등 다양한 표준 컨테이너와 함수들이 constexpr을 지원합니다.

예제: constexpr std::vector 사용

#include <vector>
#include <array>

constexpr std::vector<int> createVector() {
    std::vector<int> vec;
    for (int i = 1; i <= 5; ++i) {
        vec.push_back(i * i);
    }
    return vec;
}

int main() {
    constexpr auto vec = createVector();
    static_assert(vec.size() == 5, "벡터 크기는 5이어야 합니다.");
    static_assert(vec[2] == 9, "세 번째 요소는 9이어야 합니다.");

    return 0;
}
  • std::vector를 constexpr 함수 내에서 생성하고 조작할 수 있습니다.
  • static_assert를 통해 컴파일 타임에 검증이 가능합니다.

어떻게 좋아졌나요?

  • 컴파일 타임 데이터 처리 강화: 동적 크기의 컨테이너를 컴파일 타임에 사용할 수 있어 복잡한 상수 데이터 생성이 가능해졌습니다.
  • 코드 안전성 향상: 컴파일 타임에 오류를 발견할 수 있으므로, 런타임 오류를 줄일 수 있습니다.
  • 표준화된 방식 사용: 사용자 정의 타입 없이 표준 컨테이너를 활용하므로, 코드의 가독성과 유지 보수성이 향상됩니다.

상세한 예제와 비교

1. constexpr std::string 사용

이전 방식

// 컴파일 타임에 문자열 처리는 제한적이었음
constexpr const char* getGreeting() {
    return "Hello, World!";
}

C++20 방식

#include <string>

constexpr std::string getGreeting() {
    std::string greeting = "Hello";
    greeting += ", ";
    greeting += "World!";
    return greeting;
}

int main() {
    constexpr auto greeting = getGreeting();
    static_assert(greeting == "Hello, World!", "인사말이 일치하지 않습니다.");

    return 0;
}
  • std::string을 constexpr로 사용하여 문자열 조작이 가능해졌습니다.

2. constexpr 알고리즘 사용

#include <algorithm>
#include <array>

constexpr bool isSorted() {
    std::array<int, 5> arr = {1, 3, 2, 5, 4};
    std::sort(arr.begin(), arr.end());
    return std::is_sorted(arr.begin(), arr.end());
}

int main() {
    static_assert(isSorted(), "배열이 정렬되지 않았습니다.");
    return 0;
}
  • std::sort와 같은 알고리즘을 constexpr로 사용하여 컴파일 타임에 데이터 처리가 가능합니다.

3. 복잡한 컴파일 타임 연산

#include <vector>

constexpr int computeSum(int n) {
    std::vector<int> vec;
    for (int i = 1; i <= n; ++i) {
        vec.push_back(i);
    }
    int sum = 0;
    for (int value : vec) {
        sum += value;
    }
    return sum;
}

int main() {
    constexpr int sum = computeSum(10);
    static_assert(sum == 55, "합계가 일치하지 않습니다.");

    return 0;
}
  • std::vector를 활용하여 컴파일 타임에 합계를 계산할 수 있습니다.

주의 사항

  • 메모리 제한: 컴파일 타임에 동적 메모리를 할당하므로, 컴파일러의 메모리 제한에 유의해야 합니다.
  • 컴파일 시간 증가: 복잡한 컴파일 타임 연산은 컴파일 시간을 늘릴 수 있습니다.
  • 지원 여부 확인: 모든 컴파일러가 constexpr 확장을 완벽하게 지원하지 않을 수 있으므로, 컴파일러의 지원 여부를 확인해야 합니다.

요약

C++20에서의 constexpr 확장은 표준 라이브러리의 컨테이너와 알고리즘에까지 적용되어, 컴파일 타임 프로그래밍의 가능성을 크게 넓혔습니다. 이전에는 불가능했던 복잡한 데이터 구조의 컴파일 타임 생성을 가능하게 하여, 코드의 안전성과 효율성을 높일 수 있습니다. 이를 통해 더욱 강력한 템플릿 메타프로그래밍이 가능해졌으며, 런타임 오류를 컴파일 타임에 잡아낼 수 있게 되었습니다.

 

 

참고 자료:

반응형