C++ constexpr 고급 가이드 | constexpr 컨테이너·알고리즘·문자열·new/delete 실전

C++ constexpr 고급 가이드 | constexpr 컨테이너·알고리즘·문자열·new/delete 실전

이 글의 핵심

C++20 constexpr std::vector·std::string·알고리즘, constexpr new/delete, 컴파일 타임 컨테이너·알고리즘 완전 예제. 문제 시나리오, 자주 발생하는 에러, 베스트 프랙티스, 프로덕션 패턴.

들어가며: “컴파일 타임에 컨테이너와 알고리즘을 돌리고 싶어요”

구체적인 문제 시나리오

constexpr 기초를 익힌 뒤, 이런 고급 요구가 생깁니다:

  • 설정값 목록을 컴파일 타임에 파싱해 정렬·검증하고 싶다
  • 프로토콜 명령어 테이블을 문자열 리터럴에서 생성해 std::vector처럼 다루고 싶다
  • CRC/해시 테이블std::array가 아닌 동적 크기로 만들고 싶다
  • JSON 스키마의 필드 목록을 컴파일 타임에 정렬해 이진 검색용으로 쓰고 싶다
  • 단위 테스트에서 고정된 std::vector<int> 데이터를 컴파일 타임에 만들어 재현성을 보장하고 싶다

추가 문제 시나리오

시나리오 1: 컴파일 타임 설정 파싱
"1024,2048,4096" 같은 문자열을 컴파일 타임에 파싱해 std::vector<size_t>로 만들고, 정렬·중복 제거 후 static_assert로 검증하고 싶습니다. C++20 constexpr std::string, std::vector가 없으면 불가능했습니다.

시나리오 2: 프로토콜 명령어 디스패치 테이블
"START", "STOP", "PAUSE" 등 문자열 리터럴을 컴파일 타임에 해시해 std::array에 넣고, 런타임에는 해시만 계산해 O(1) 테이블 조회로 명령을 처리하고 싶습니다. constexpr 알고리즘으로 테이블을 정렬해 이진 검색도 가능합니다.

시나리오 3: 임베디드 메모리 맵 검증
레지스터 오프셋 배열이 오름차순인지, 겹치는 영역이 없는지 컴파일 타임에 검증하고 싶습니다. constexpr std::sort와 반복문으로 검증 로직을 짜면, 잘못된 맵은 컴파일 에러로 잡을 수 있습니다.

시나리오 4: 테스트 픽스처 생성
단위 테스트에서 {1, 2, 3, 5, 8, 13} 같은 피보나치 시퀀스를 컴파일 타임에 생성해, 런타임 초기화 비용 없이 테스트를 돌리고 싶습니다.

시나리오 5: 컴파일 타임 Base64 디코딩
"SGVsbG8=" 같은 Base64 문자열을 컴파일 타임에 디코딩해 std::string 또는 std::vector<uint8_t>로 만들고, 런타임에는 그 결과만 사용하고 싶습니다.

C++20에서는 constexpr std::vector, constexpr std::string, constexpr 알고리즘, constexpr new/delete가 표준에 들어와, 위 시나리오들을 모두 구현할 수 있습니다.

C++20 constexpr 확장 개요

flowchart TD
    A[C++20 constexpr 확장] --> B["std vector"]
    A --> C["std string"]
    A --> D[알고리즘: sort, find, copy...]
    A --> E[new/delete]
    B --> F[컴파일 타임 동적 컨테이너]
    C --> G[컴파일 타임 문자열 조작]
    D --> H[컴파일 타임 정렬·검색]
    E --> I[컴파일 타임 동적 할당]

이 글을 읽으면

  • constexpr std::vector, std::string, std::array를 컴파일 타임에 다룰 수 있습니다.
  • constexpr std::sort, std::find 등 알고리즘을 컴파일 타임에 사용할 수 있습니다.
  • constexpr new/delete의 동작과 제약을 이해할 수 있습니다.
  • 자주 발생하는 에러와 해결법을 알 수 있습니다.
  • 프로덕션에서 바로 쓸 수 있는 고급 패턴을 적용할 수 있습니다.

개념을 잡는 비유

템플릿 인자 자리는 붕어빵 틀의 칸 수가 정해지듯, 컴파일 시점에 크기·상수가 박혀 있어야 하는 경우가 많습니다. constexpr·컴파일 타임 계산은 그 값을 미리 찍어내어, 배열 크기와 static_assert 같은 곳에 그대로 얹을 수 있게 해 줍니다.


목차

  1. 문제 시나리오 상세
  2. constexpr 컨테이너
  3. constexpr 알고리즘
  4. constexpr 문자열
  5. constexpr new/delete (C++20)
  6. 자주 발생하는 에러와 해결법
  7. 베스트 프랙티스
  8. 프로덕션 패턴
  9. 성능 및 컴파일 타임 비용

1. 문제 시나리오 상세

시나리오 A: 컴파일 타임 설정 검증

문제: 버퍼 크기 목록 "64,128,256,512"를 파싱해, 모두 2의 거듭제곱인지, 오름차순인지 컴파일 타임에 검증하고 싶습니다.

C++17 이하: std::vector가 constexpr가 아니어서, 파싱 결과를 컴파일 타임에 담을 수 없었습니다. std::array로 고정 크기를 쓰거나, 런타임 검증만 가능했습니다.

C++20 해법: constexpr std::vector로 파싱 → 정렬 → 검증을 모두 컴파일 타임에 수행합니다.

// C++20: g++ -std=c++20 -o parse parse.cpp
#include <algorithm>
#include <cstddef>
#include <string>
#include <vector>

constexpr std::vector<std::size_t> parse_sizes(const char* s) {
    std::vector<std::size_t> result;
    std::size_t val = 0;
    while (*s) {
        if (*s >= '0' && *s <= '9') {
            val = val * 10 + (*s - '0');
        } else if (*s == ',') {
            result.push_back(val);
            val = 0;
        }
        ++s;
    }
    if (val != 0) result.push_back(val);
    return result;
}

constexpr bool is_power_of_two(std::size_t n) {
    return n > 0 && (n & (n - 1)) == 0;
}

constexpr bool validate_sizes(const std::vector<std::size_t>& v) {
    auto sorted = v;
    std::sort(sorted.begin(), sorted.end());
    for (std::size_t i = 0; i < sorted.size(); ++i) {
        if (!is_power_of_two(sorted[i])) return false;
        if (i > 0 && sorted[i] <= sorted[i - 1]) return false;
    }
    return true;
}

int main() {
    constexpr auto sizes = parse_sizes("64,128,256,512");
    static_assert(validate_sizes(sizes));
    return 0;
}

시나리오 B: 프로토콜 명령어 해시 테이블

문제: "START", "STOP", "PAUSE", "RESUME"을 컴파일 타임에 해시해, 런타임에는 해시만 계산해 O(1) 조회로 명령을 처리하고 싶습니다.

// C++20
#include <array>
#include <cstddef>
#include <string_view>

constexpr std::size_t hash_fnv1a(const char* str) {
    std::size_t hash = 14695981039346656037ULL;
    while (*str) {
        hash ^= static_cast<unsigned char>(*str++);
        hash *= 1099511628211ULL;
    }
    return hash;
}

template <std::size_t N>
constexpr auto make_command_table(const char* const (&commands)[N]) {
    std::array<std::pair<std::size_t, std::size_t>, N> table;
    for (std::size_t i = 0; i < N; ++i) {
        table[i] = {hash_fnv1a(commands[i]), i};
    }
    std::sort(table.begin(), table.end());
    return table;
}

constexpr const char* COMMANDS[] = {"START", "STOP", "PAUSE", "RESUME"};
constexpr auto CMD_TABLE = make_command_table(COMMANDS);

int dispatch(std::string_view cmd) {
    auto h = hash_fnv1a(cmd.data());
    auto it = std::lower_bound(CMD_TABLE.begin(), CMD_TABLE.end(), h,
         { return p.first < key; });
    if (it != CMD_TABLE.end() && it->first == h)
        return static_cast<int>(it->second);
    return -1;
}

시나리오 C: 컴파일 타임 피보나치 시퀀스

문제: 테스트 픽스처로 {1, 1, 2, 3, 5, 8, 13, 21}을 컴파일 타임에 생성하고 싶습니다.

// C++20
#include <vector>

constexpr std::vector<int> fib_sequence(int n) {
    std::vector<int> result;
    if (n >= 1) result.push_back(1);
    if (n >= 2) result.push_back(1);
    for (int i = 2; i < n; ++i) {
        result.push_back(result[i - 1] + result[i - 2]);
    }
    return result;
}

// 컴파일 타임에 8개 피보나치 수 생성
constexpr auto FIB8 = fib_sequence(8);
// FIB8 = {1, 1, 2, 3, 5, 8, 13, 21}

2. constexpr 컨테이너

2.1 constexpr std::array (C++17)

std::array는 C++17부터 constexpr 생성자와 멤버 함수가 있어, 컴파일 타임에 완전히 사용할 수 있습니다.

#include <array>
#include <algorithm>

// 컴파일 타임에 배열 생성 및 정렬
constexpr std::array<int, 5> make_sorted() {
    std::array<int, 5> a = {5, 2, 8, 1, 9};
    std::sort(a.begin(), a.end());
    return a;
}

constexpr auto SORTED = make_sorted();
static_assert(SORTED[0] == 1);
static_assert(SORTED[4] == 9);

2.2 constexpr std::vector (C++20)

C++20에서 std::vector는 constexpr 생성자, push_back, resize, 소멸자 등이 constexpr로 확장되었습니다.

// C++20
#include <vector>

constexpr std::vector<int> make_vector() {
    std::vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    return v;
}

constexpr auto V = make_vector();
static_assert(V.size() == 3);
static_assert(V[0] == 1 && V[1] == 2 && V[2] == 3);

주의: constexpr 평가 중 할당된 메모리는 평가 종료 시 자동 해제됩니다. constexpr auto V = make_vector();에서 V는 평가 결과로 이 복사되며, std::vector의 내부 동적 메모리는 컴파일 타임 평가용으로만 쓰입니다. 최종 V는 컴파일러가 상수로 초기화한 std::vector입니다.

2.3 constexpr std::vector 고급 예제: 동적 크기 테이블

// C++20: 컴파일 타임에 N개 항목 테이블 생성
template <typename Func>
constexpr auto make_table(size_t n, Func f) {
    std::vector<decltype(f(0))> result;
    result.reserve(n);
    for (size_t i = 0; i < n; ++i) {
        result.push_back(f(i));
    }
    return result;
}

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

constexpr auto SQUARES = make_table(10, square);
// SQUARES = {0, 1, 4, 9, 16, 25, 36, 49, 64, 81}
static_assert(SQUARES.size() == 10);
static_assert(SQUARES[5] == 25);

2.4 constexpr 컨테이너 제약 사항

항목std::arraystd::vector (C++20)
constexpr 생성
push_back / resizeN/A (고정 크기)
반복자, operator[]
allocator기본만기본만 (커스텀 allocator는 제한)
예외constexpr 평가 중 throw 불가동일

3. constexpr 알고리즘

3.1 C++20 constexpr 알고리즘 목록

C++20에서 다음 알고리즘들이 constexpr로 확장되었습니다:

  • std::sort, std::stable_sort
  • std::find, std::find_if, std::find_if_not
  • std::copy, std::copy_if, std::fill
  • std::min_element, std::max_element
  • std::lower_bound, std::upper_bound, std::binary_search
  • std::reverse, std::rotate
  • std::all_of, std::any_of, std::none_of
  • 기타 다수

3.2 constexpr 정렬 예제

// C++20
#include <algorithm>
#include <array>

constexpr std::array<int, 6> sort_at_compile_time() {
    std::array<int, 6> a = {6, 2, 9, 1, 5, 3};
    std::sort(a.begin(), a.end());
    return a;
}

constexpr auto SORTED_ARR = sort_at_compile_time();
static_assert(SORTED_ARR[0] == 1);
static_assert(SORTED_ARR[5] == 9);

3.3 constexpr 이진 검색

// C++20: 컴파일 타임에 정렬된 배열에서 이진 검색
#include <algorithm>
#include <array>

constexpr std::array<int, 5> SORTED = {1, 3, 5, 7, 9};

constexpr bool contains(int key) {
    return std::binary_search(SORTED.begin(), SORTED.end(), key);
}

static_assert(contains(5));
static_assert(!contains(4));

3.4 constexpr std::vector + std::sort

// C++20
#include <algorithm>
#include <vector>

constexpr std::vector<int> sorted_vector() {
    std::vector<int> v = {30, 10, 50, 20, 40};
    std::sort(v.begin(), v.end());
    return v;
}

constexpr auto SV = sorted_vector();
static_assert(SV[0] == 10);
static_assert(SV[4] == 50);

3.5 constexpr all_of / any_of

// C++20
#include <algorithm>
#include <array>

constexpr std::array<int, 4> EVENS = {2, 4, 6, 8};

constexpr bool all_even() {
    return std::all_of(EVENS.begin(), EVENS.end(),
         { return x % 2 == 0; });
}

static_assert(all_even());

4. constexpr 문자열

4.1 constexpr std::string (C++20)

C++20에서 std::string의 생성자, operator+=, push_back, substr, find 등이 constexpr로 확장되었습니다.

// C++20
#include <string>

constexpr std::string make_greeting(const char* name) {
    std::string result = "Hello, ";
    result += name;
    result += "!";
    return result;
}

constexpr auto GREETING = make_greeting("World");
static_assert(GREETING == "Hello, World!");
static_assert(GREETING.size() == 12);

4.2 constexpr 문자열 파싱

// C++20: "key=value" 형식 파싱
#include <string>
#include <utility>

constexpr std::pair<std::string, std::string> parse_key_value(const char* s) {
    std::string key, value;
    while (*s && *s != '=') {
        key += *s++;
    }
    if (*s == '=') ++s;
    while (*s) {
        value += *s++;
    }
    return {key, value};
}

constexpr auto KV = parse_key_value("size=4096");
static_assert(KV.first == "size");
static_assert(KV.second == "4096");

4.3 constexpr 문자열 검색 및 조작

// C++20
#include <string>

constexpr std::string replace_first(const std::string& s,
    const std::string& from, const std::string& to) {
    std::string result = s;
    auto pos = result.find(from);
    if (pos != std::string::npos) {
        result.replace(pos, from.size(), to);
    }
    return result;
}

constexpr auto R = replace_first("foo_bar_baz", "_", "-");
static_assert(R == "foo-bar_baz");

4.4 constexpr std::string_view

std::string_view는 C++17부터 constexpr 생성자가 있으며, C++20에서 substr, find 등이 constexpr로 확장되었습니다.

// C++20
#include <string_view>

constexpr std::string_view trim_leading(std::string_view sv) {
    while (!sv.empty() && sv[0] == ' ') {
        sv.remove_prefix(1);
    }
    return sv;
}

constexpr auto TRIMMED = trim_leading("  hello");
static_assert(TRIMMED == "hello");

5. constexpr new/delete (C++20)

5.1 기본 개념

C++20에서 constexpr 평가 중 newdelete가 허용됩니다. 할당된 메모리는 평가가 끝나면 자동으로 해제되므로, 런타임 메모리 누수와 무관합니다.

// C++20
constexpr int* dynamic_sum(int a, int b) {
    int* p = new int(a + b);
    int result = *p;
    delete p;
    return new int(result);  // 주의: 이 포인터는 평가 종료 후 유효하지 않음
}

// 실제로는 값만 필요하므로 이렇게 쓰는 것이 좋음
constexpr int dynamic_sum_safe(int a, int b) {
    int* p = new int(a + b);
    int result = *p;
    delete p;
    return result;
}

constexpr int x = dynamic_sum_safe(3, 5);
static_assert(x == 8);

5.2 constexpr new/delete 규칙

  1. 평가 종료 시 모든 할당이 해제되어야 함: 평가가 끝날 때 new로 할당된 메모리가 남아 있으면 ill-formed입니다.
  2. placement new는 제한적: 일반 new/delete만 constexpr 평가에 사용할 수 있습니다.
  3. 예외: constexpr 평가 중 new가 실패하면 예외를 던질 수 있지만, constexpr 맥락에서는 예외가 허용되지 않는 경우가 많아 실질적으로 실패 시 컴파일 에러가 됩니다.

5.3 constexpr 동적 배열 예제

// C++20: 컴파일 타임에 동적 크기 배열 생성
constexpr int* make_dynamic_array(size_t n) {
    int* p = new int[n];
    for (size_t i = 0; i < n; ++i) {
        p[i] = static_cast<int>(i * i);
    }
    return p;
}

// 주의: make_dynamic_array가 반환하는 포인터는 평가 종료 후 유효하지 않음
// 따라서 값만 추출해 사용
constexpr int get_square_at(size_t n) {
    int* p = make_dynamic_array(n + 1);
    int result = p[n];
    delete[] p;
    return result;
}

static_assert(get_square_at(5) == 25);

5.4 constexpr new를 활용한 벡터 스타일 구현

// C++20: constexpr 평가 중 new/delete로 동적 배열 시뮬레이션
constexpr size_t count_digits(unsigned long long n) {
    if (n == 0) return 1;
    size_t count = 0;
    while (n) {
        ++count;
        n /= 10;
    }
    return count;
}

constexpr auto DIGIT_COUNT = count_digits(12345);
static_assert(DIGIT_COUNT == 5);

5.5 constexpr new/delete 제약 요약

항목허용
new T
new T[n]
delete p
delete[] p
placement new❌ (일반적으로)
커스텀 allocator제한적
평가 종료 시 미해제 메모리❌ ill-formed

6. 자주 발생하는 에러와 해결법

에러 1: C++17에서 std::vector/std::string을 constexpr로 사용

원인: std::vectorstd::string의 constexpr 지원은 C++20부터입니다.

// ❌ C++17에서 컴파일 에러
// constexpr std::vector<int> v = {1, 2, 3};

// ✅ C++20 사용
// g++ -std=c++20 ...
constexpr std::vector<int> v = {1, 2, 3};

해결: -std=c++20 이상을 사용하고, C++17이 필수라면 std::array로 대체합니다.

에러 2: constexpr 평가 중 throw

원인: constexpr 평가 맥락에서는 예외를 던지면 안 됩니다.

// ❌ 컴파일 에러
// constexpr int bad(int x) {
//     if (x < 0) throw std::invalid_argument("negative");
//     return x;
// }

// ✅ 조건 검사 후 에러 시 컴파일 실패 유도
constexpr int good(int x) {
    if (x < 0) {
        // constexpr에서 throw 불가 → static_assert로 대체
        return -1;  // 또는 조건을 static_assert로 검증
    }
    return x;
}

template <int X>
void use() {
    static_assert(X >= 0, "X must be non-negative");
}

에러 3: constexpr new 후 delete 누락

원인: constexpr 평가가 끝날 때 new로 할당된 메모리가 남아 있으면 ill-formed입니다.

// ❌ 컴파일 에러: 메모리 누수
// constexpr int* leak() {
//     return new int(42);
// }

// ✅ 반드시 delete
constexpr int safe() {
    int* p = new int(42);
    int r = *p;
    delete p;
    return r;
}

에러 4: constexpr 함수에서 I/O 또는 전역 상태

원인: constexpr 평가 중에는 std::cout, 파일 입출력, 전역 변수 수정이 불가능합니다.

// ❌ 컴파일 에러
// constexpr int bad() {
//     std::cout << "hello";
//     return 42;
// }

// ✅ 순수 계산만
constexpr int good(int x) {
    return x * 2;
}

에러 5: constexpr std::string에 런타임 포인터 전달

원인: consteval이 아닌 constexpr 함수에 런타임 문자열을 넘기면, 해당 호출은 런타임에 실행됩니다. 상수 식이 필요한 곳에 그 결과를 넣으면 에러가 납니다.

constexpr std::string concat(const char* a, const char* b) {
    std::string s = a;
    s += b;
    return s;
}

int main() {
    const char* rt = "runtime";  // 런타임 포인터
    // static_assert(concat(rt, "x").size() > 0);  // ❌ 에러
    auto s = concat("hello", "world");  // ✅ 런타임 호출 OK
    constexpr auto c = concat("a", "b");  // ✅ 컴파일 타임
    static_assert(c == "ab");
    return 0;
}

에러 6: 컴파일러별 constexpr 한계

원인: GCC, Clang, MSVC마다 constexpr 평가 단계 수, 재귀 깊이, std::vector/std::string 지원 정도가 다릅니다.

컴파일러constexpr 단계 기본값constexpr vector/string
GCC 10+512
Clang 10+512
MSVC 2019 16.8+제한적✅ (일부)

해결: -fconstexpr-ops-limit, -fconstexpr-depth 등으로 조정할 수 있으나, 복잡한 constexpr는 단순화하는 것이 이식성에 좋습니다.

에러 7: constexpr 알고리즘에 비교 불가 타입

원인: std::sort 등은 operator< 또는 비교 함수가 필요합니다. constexpr 평가 중에는 해당 연산이 constexpr 가능해야 합니다.

// ✅ int 등 기본 타입은 문제없음
constexpr std::array<int, 3> a = {3, 1, 2};
std::sort(a.begin(), a.end());  // OK

// 사용자 정의 타입은 constexpr operator< 필요
struct Point {
    int x, y;
    constexpr bool operator<(const Point& o) const {
        return x < o.x || (x == o.x && y < o.y);
    }
};

에러 8: constexpr vector의 allocator

원인: constexpr std::vector는 기본 std::allocator만 지원합니다. 커스텀 allocator를 쓰면 constexpr가 아닐 수 있습니다.

// ❌ 커스텀 allocator는 constexpr 제한
// std::vector<int, MyAllocator<int>> v;

// ✅ 기본 allocator 사용
constexpr std::vector<int> v = {1, 2, 3};

7. 베스트 프랙티스

1. C++20 이상에서 constexpr 컨테이너 활용

std::vector, std::string을 컴파일 타임에 쓰려면 반드시 C++20을 사용합니다. C++17 이하에서는 std::array와 고정 크기 버퍼로 대체합니다.

2. constexpr new는 꼭 필요한 경우만

constexpr new/delete는 복잡도를 높이고 컴파일러 부담을 늘립니다. std::vector로 대체 가능하면 std::vector를 우선합니다.

// ✅ 권장: std::vector
constexpr std::vector<int> make_data() {
    std::vector<int> v;
    for (int i = 0; i < 10; ++i) v.push_back(i * i);
    return v;
}

// ⚠️ new/delete는 꼭 필요할 때만
constexpr int with_new() {
    int* p = new int(42);
    int r = *p;
    delete p;
    return r;
}

3. 컴파일 타임 검증은 static_assert와 함께

constexpr로 계산한 결과는 static_assert로 검증해, 잘못된 설정이면 컴파일 단계에서 막습니다.

constexpr int parse_buffer_size(const char* s) {
    int r = 0;
    while (*s >= '0' && *s <= '9') {
        r = r * 10 + (*s - '0');
        ++s;
    }
    return r;
}

constexpr int BUF = parse_buffer_size("4096");
static_assert(BUF >= 64 && BUF <= 65536, "buffer size out of range");

4. 반복문 선호, 깊은 재귀 지양

constexpr 재귀가 깊어지면 컴파일러 한계에 걸릴 수 있습니다. 반복문으로 전환하면 이식성이 좋습니다.

// ✅ 권장: 반복문
constexpr int sum_loop(int n) {
    int s = 0;
    for (int i = 0; i <= n; ++i) s += i;
    return s;
}

// ⚠️ 깊은 재귀는 컴파일러 한계
// constexpr int sum_recursive(int n) {
//     return n <= 0 ? 0 : n + sum_recursive(n - 1);
// }

5. constexpr 결과는 constexpr 변수에 저장

컴파일 타임에 계산된 값을 여러 곳에서 쓰려면 constexpr 변수로 저장해 재사용합니다.

constexpr auto TABLE = make_crc32_table();
// 이후 TABLE을 여러 함수에서 사용

6. 컴파일 타임 전용이면 consteval

“반드시 컴파일 타임에만” 평가되어야 하는 함수는 consteval로 선언해, 실수로 런타임 호출하는 것을 막습니다.

consteval int must_be_compile_time(int x) {
    return x * 2;
}

8. 프로덕션 패턴

패턴 1: 컴파일 타임 설정 파싱 + 검증

// C++20: "key1=val1,key2=val2" 파싱 후 검증
#include <string>
#include <vector>

constexpr std::vector<std::pair<std::string, std::string>>
parse_config(const char* s) {
    std::vector<std::pair<std::string, std::string>> result;
    std::string key, value;
    enum { Key, Value } state = Key;
    while (*s) {
        if (*s == '=') {
            state = Value;
            ++s;
            continue;
        }
        if (*s == ',') {
            result.push_back({key, value});
            key.clear();
            value.clear();
            state = Key;
            ++s;
            continue;
        }
        if (state == Key) key += *s;
        else value += *s;
        ++s;
    }
    if (!key.empty()) result.push_back({key, value});
    return result;
}

constexpr auto CONFIG = parse_config("size=4096,timeout=30");
static_assert(CONFIG.size() == 2);

패턴 2: 컴파일 타임 정렬된 명령어 테이블

// C++20: 명령어 문자열 → 인덱스 매핑 (정렬 후 이진 검색)
#include <algorithm>
#include <array>
#include <string_view>

template <size_t N>
constexpr auto make_sorted_command_table(const char* const (&cmds)[N]) {
    std::array<std::pair<std::string_view, size_t>, N> table;
    for (size_t i = 0; i < N; ++i) {
        table[i] = {cmds[i], i};
    }
    std::sort(table.begin(), table.end(),
         { return a.first < b.first; });
    return table;
}

constexpr const char* COMMANDS[] = {"PAUSE", "RESUME", "START", "STOP"};
constexpr auto CMD_TABLE = make_sorted_command_table(COMMANDS);

패턴 3: 컴파일 타임 Base64 디코딩 (간략)

// C++20: Base64 디코딩 (개념 예시)
#include <cstdint>
#include <string>
#include <vector>

constexpr int b64_index(char c) {
    if (c >= 'A' && c <= 'Z') return c - 'A';
    if (c >= 'a' && c <= 'z') return c - 'a' + 26;
    if (c >= '0' && c <= '9') return c - '0' + 52;
    if (c == '+') return 62;
    if (c == '/') return 63;
    return -1;
}

constexpr std::vector<uint8_t> b64_decode(std::string_view sv) {
    std::vector<uint8_t> out;
    int val = 0, bits = 0;
    for (char c : sv) {
        if (c == '=') break;
        int idx = b64_index(c);
        if (idx < 0) continue;
        val = (val << 6) | idx;
        bits += 6;
        if (bits >= 8) {
            bits -= 8;
            out.push_back(static_cast<uint8_t>((val >> bits) & 0xFF));
            val &= (1 << bits) - 1;
        }
    }
    return out;
}

constexpr auto DECODED = b64_decode("SGVsbG8=");
static_assert(DECODED.size() == 5);
static_assert(DECODED[0] == 'H' && DECODED[4] == 'o');

패턴 4: 타입별 constexpr 상수

template <typename T>
struct type_constants;

template <>
struct type_constants<int> {
    static constexpr size_t max_digits = 10;
    static constexpr int min_val = -2147483648;
    static constexpr int max_val = 2147483647;
};

template <>
struct type_constants<long long> {
    static constexpr size_t max_digits = 19;
};

template <typename T>
constexpr size_t buffer_size_for = type_constants<T>::max_digits + 2;

패턴 5: constexpr 레지스터 맵 검증

// C++20: 레지스터 오프셋 배열이 오름차순인지 검증
#include <algorithm>
#include <array>

constexpr std::array<uint32_t, 4> REG_OFFSETS = {0x00, 0x04, 0x08, 0x0C};

constexpr bool validate_reg_map() {
    auto sorted = REG_OFFSETS;
    std::sort(sorted.begin(), sorted.end());
    for (size_t i = 0; i < sorted.size(); ++i) {
        if (sorted[i] != REG_OFFSETS[i]) return false;
        if (i > 0 && sorted[i] - sorted[i-1] != 4) return false;
    }
    return true;
}

static_assert(validate_reg_map());

패턴 6: 컴파일 타임 테스트 픽스처

// C++20: 테스트용 고정 데이터
constexpr std::vector<int> make_test_fixture() {
    std::vector<int> v;
    v.reserve(10);
    for (int i = 0; i < 10; ++i) {
        v.push_back(i * 11);
    }
    return v;
}

constexpr auto TEST_DATA = make_test_fixture();
// 단위 테스트에서 TEST_DATA 사용, 런타임 초기화 비용 없음

9. 성능 및 컴파일 타임 비용

9.1 런타임 비용

constexpr로 컴파일 타임에 계산된 값은 런타임 비용이 0입니다. constexpr auto V = make_vector();V는 컴파일 시점에 이미 초기화된 객체이므로, 프로그램 시작 시 추가 계산이 없습니다.

9.2 컴파일 타임 비용

constexpr 평가가 복잡할수록 컴파일 시간이 늘어납니다. 대규모 constexpr std::vector/std::string 연산, 반복적인 std::sort 등은 컴파일을 느리게 할 수 있습니다.

패턴컴파일 영향
단순 constexpr 함수 (팩토리얼 등)미미
std::array + std::sort작음
std::vector + 여러 push_back + sort중간
큰 문자열 파싱, 반복 new/delete

9.3 권장 사항

  • 필요한 범위만 constexpr로 처리
  • 너무 큰 데이터는 런타임 초기화 고려
  • 빌드 시간이 중요하면 constexpr 사용량을 조절

constexpr 고급 적용 체크리스트

  • C++20 이상 사용 (-std=c++20)
  • std::vector/std::string constexpr 사용 시 C++20 확인
  • constexpr new 사용 시 반드시 delete 호출
  • 컴파일 타임 검증은 static_assert와 함께
  • 반복문 우선, 깊은 재귀 지양
  • 컴파일 타임 전용 함수는 consteval 고려
  • 모든 코드 블록에 cpp 언어 태그 사용

정리

항목내용
constexpr std::vectorC++20, push_back·resize·정렬 등 컴파일 타임 지원
constexpr std::stringC++20, 파싱·연결·검색 등 컴파일 타임 지원
constexpr 알고리즘C++20, sort·find·copy·binary_search 등
constexpr new/deleteC++20, 평가 종료 시 자동 해제
자주 발생하는 에러C++17 사용, throw, new 누락, I/O 등
베스트 프랙티스static_assert 검증, 반복문 선호, consteval 활용
프로덕션 패턴설정 파싱, 명령어 테이블, Base64, 레지스터 검증

같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.

  • C++ constexpr 완벽 가이드 | 컴파일 타임 계산·if constexpr·consteval 실전
  • C++ 컴파일 타임 프로그래밍 기법 | 런타임 오버헤드 제거와 constexpr·consteval 실전
  • C++ 컴파일 타임 최적화 | constexpr·PCH·모듈·ccache·Unity 빌드 [#15-3]

이 글에서 다루는 키워드

C++ constexpr 고급, constexpr vector, constexpr string, constexpr 알고리즘, constexpr new delete, C++20 constexpr, 컴파일 타임 컨테이너 등으로 검색하시면 이 글이 도움이 됩니다.


한 줄 요약: C++20 constexpr std::vector·std::string·알고리즘·new/delete로 컴파일 타임에 동적 컨테이너와 알고리즘을 실행할 수 있습니다. 설정 파싱, 명령어 테이블, Base64 디코딩 등 실전 패턴에 활용하세요.

실전 체크리스트

실무에서 이 개념을 적용할 때 확인해야 할 사항입니다.

코드 작성 전

  • 이 기법이 현재 문제를 해결하는 최선의 방법인가?
  • 팀원들이 이 코드를 이해하고 유지보수할 수 있는가?
  • 성능 요구사항을 만족하는가?

코드 작성 중

  • 컴파일러 경고를 모두 해결했는가?
  • 엣지 케이스를 고려했는가?
  • 에러 처리가 적절한가?

코드 리뷰 시

  • 코드의 의도가 명확한가?
  • 테스트 케이스가 충분한가?
  • 문서화가 되어 있는가?

이 체크리스트를 활용하여 실수를 줄이고 코드 품질을 높이세요.


자주 묻는 질문 (FAQ)

Q. 이 내용을 실무에서 언제 쓰나요?

A. C++20 constexpr std::vector·std::string·알고리즘, constexpr new/delete, 컴파일 타임 컨테이너·알고리즘 완전 예제. 문제 시나리오, 자주 발생하는 에러, 베스트 프랙티… 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.

Q. 선행으로 읽으면 좋은 글은?

A. 각 글 하단의 이전 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.

Q. 더 깊이 공부하려면?

A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.

이전 글: C++ constexpr 기초 (#43-1)


관련 글

  • C++ constexpr 완벽 가이드 | 컴파일 타임 계산·if constexpr·consteval 실전
  • C++ 고성능 RPC 시스템: gRPC와 Protocol Buffers를 이용한 마이크로서비스 구축
  • C++ 보안 코딩 가이드: 오버플로우 방지와 암호화 라이브러리(OpenSSL) 실전 연동 [#43-2]
  • C++ Observability: Prometheus와 Grafana로 C++ 서버 모니터링 구축하기
  • C++ 메타프로그래밍의 진화: Template에서 Constexpr, 그리고 Reflection까지