C++ if constexpr | "컴파일 타임 if" 가이드

C++ if constexpr | "컴파일 타임 if" 가이드

이 글의 핵심

C++ if constexpr에 대한 실전 가이드입니다. 개념부터 실무 활용까지 예제와 함께 상세히 설명합니다.

if constexpr란?

컴파일 타임에 분기 결정 (C++17)

template<typename T>
auto getValue(T value) {
    if constexpr (std::is_pointer_v<T>) {
        return *value;  // 포인터
    } else {
        return value;   // 값
    }
}

기본 사용

template<typename T>
void print(T value) {
    if constexpr (std::is_integral_v<T>) {
        std::cout << "정수: " << value << std::endl;
    } else if constexpr (std::is_floating_point_v<T>) {
        std::cout << "실수: " << value << std::endl;
    } else {
        std::cout << "기타: " << value << std::endl;
    }
}

print(10);     // "정수: 10"
print(3.14);   // "실수: 3.14"
print("Hi");   // "기타: Hi"

실전 예시

예시 1: 타입별 처리

#include <type_traits>
#include <string>

template<typename T>
std::string toString(T value) {
    if constexpr (std::is_same_v<T, bool>) {
        return value ? "true" : "false";
    } else if constexpr (std::is_arithmetic_v<T>) {
        return std::to_string(value);
    } else if constexpr (std::is_convertible_v<T, std::string>) {
        return value;
    } else {
        return "unknown";
    }
}

int main() {
    std::cout << toString(true) << std::endl;    // "true"
    std::cout << toString(42) << std::endl;      // "42"
    std::cout << toString("Hello") << std::endl; // "Hello"
}

예시 2: 재귀 종료

template<typename T, typename... Rest>
void print(T first, Rest... rest) {
    std::cout << first;
    
    if constexpr (sizeof...(rest) > 0) {
        std::cout << ", ";
        print(rest...);  // 재귀
    } else {
        std::cout << std::endl;
    }
}

int main() {
    print(1, 2, 3, 4, 5);  // 1, 2, 3, 4, 5
}

예시 3: 최적화

template<typename T>
T multiply(T a, T b) {
    if constexpr (std::is_integral_v<T>) {
        // 정수: 비트 시프트 최적화
        if (b == 2) return a << 1;
        if (b == 4) return a << 2;
    }
    return a * b;
}

예시 4: SFINAE 대체

// ❌ SFINAE (복잡)
template<typename T>
std::enable_if_t<std::is_integral_v<T>, T>
process(T value) {
    return value * 2;
}

// ✅ if constexpr (간단)
template<typename T>
auto process(T value) {
    if constexpr (std::is_integral_v<T>) {
        return value * 2;
    } else {
        return value;
    }
}

if vs if constexpr

template<typename T>
void func(T value) {
    // 런타임 if: 모든 분기 컴파일
    if (std::is_integral_v<T>) {
        // value.length();  // 에러: int에 없음
    }
    
    // 컴파일 타임 if: 선택된 분기만 컴파일
    if constexpr (std::is_integral_v<T>) {
        return value * 2;
    } else {
        return value.length();  // OK: 정수 아닐 때만
    }
}

자주 발생하는 문제

문제 1: 비템플릿 함수

// ❌ 일반 함수에서 사용
void func(int x) {
    if constexpr (x > 0) {  // 에러: x는 런타임 값
        // ...
    }
}

// ✅ constexpr 값
constexpr int MAX = 100;
void func() {
    if constexpr (MAX > 0) {  // OK
        // ...
    }
}

문제 2: 모든 분기 문법 검사

template<typename T>
void func(T value) {
    if constexpr (std::is_integral_v<T>) {
        return value * 2;
    } else {
        return value.foo();  // 문법 검사됨
    }
}

// func<int>(10);  // 에러: int에 foo() 없음 (문법 검사)

문제 3: 중첩

template<typename T>
void func(T value) {
    if constexpr (std::is_pointer_v<T>) {
        if constexpr (std::is_const_v<std::remove_pointer_t<T>>) {
            // const 포인터
        } else {
            // 비const 포인터
        }
    }
}

문제 4: 반환 타입

template<typename T>
auto func(T value) {
    if constexpr (std::is_integral_v<T>) {
        return value * 2;      // int
    } else {
        return value * 2.0;    // double
    }
}

// 반환 타입이 T에 따라 다름

활용 패턴

// 1. 타입 변환
template<typename T>
auto convert(T value) {
    if constexpr (std::is_same_v<T, std::string>) {
        return value;
    } else {
        return std::to_string(value);
    }
}

// 2. 컨테이너 처리
template<typename Container>
void process(Container& c) {
    if constexpr (requires { c.reserve(10); }) {
        c.reserve(100);  // vector, string
    }
}

// 3. 최적화
template<size_t N>
void func() {
    if constexpr (N < 10) {
        // 작은 N: 간단한 구현
    } else {
        // 큰 N: 최적화된 구현
    }
}

FAQ

Q1: if constexpr는 언제?

A: C++17. 컴파일 타임 분기.

Q2: if와 차이?

A:

  • if: 런타임, 모든 분기 컴파일
  • if constexpr: 컴파일 타임, 선택된 분기만

Q3: 성능?

A: 런타임 비용 없음.

Q4: SFINAE 대체?

A: 가능. 더 간단함.

Q5: 비템플릿 함수?

A: constexpr 값만 사용 가능.

Q6: if constexpr 학습 리소스는?

A:

  • “C++17 The Complete Guide”
  • “Effective Modern C++”
  • cppreference.com

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

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

  • C++ constexpr Lambda | “컴파일 타임 람다” 가이드
  • C++ CTAD | “클래스 템플릿 인자 추론” 가이드
  • C++ constexpr 함수 | “컴파일 타임 함수” 가이드

관련 글

  • C++ CTAD |
  • C++ constexpr Lambda |
  • C++ Deduction Guides |
  • C++ any |
  • C++ auto 타입 추론 | 복잡한 타입을 컴파일러에 맡기기