콘텐츠로 이동

if constexpr와 컴파일 타임 분기

C++17 이전에는 타입에 따라 다른 코드를 실행하려면 SFINAE나 템플릿 특수화가 필요했습니다.

// SFINAE — 복잡하고 읽기 어려움
template<typename T>
auto serialize(T val) -> std::enable_if_t<std::is_integral_v<T>, std::string> {
return std::to_string(val);
}
template<typename T>
auto serialize(T val) -> std::enable_if_t<std::is_floating_point_v<T>, std::string> {
return std::to_string(val);
}

if constexpr는 조건을 컴파일 타임에 평가하고, 거짓인 분기의 코드를 인스턴스화하지 않습니다.

template<typename T>
std::string serialize(T val) {
if constexpr (std::is_integral_v<T>) {
return std::to_string(val);
} else if constexpr (std::is_floating_point_v<T>) {
return std::to_string(val);
} else {
return val.to_string(); // T가 정수/실수가 아닐 때만 인스턴스화
}
}

일반 if와 달리, 선택되지 않은 분기 내의 코드는 문법 검사만 받고 컴파일되지 않습니다.

template<typename T>
void print(T val) {
if (std::is_integral_v<T>) {
// T가 std::string일 때도 이 줄이 컴파일됨 → 오류 가능
std::cout << val * 2;
}
}
template<typename T>
void print2(T val) {
if constexpr (std::is_integral_v<T>) {
std::cout << val * 2; // T가 integral이 아니면 무시됨
}
}
template<typename T, typename... Ts>
void print_all(T first, Ts... rest) {
std::cout << first << '\n';
if constexpr (sizeof...(rest) > 0) {
print_all(rest...); // 0개일 때 인스턴스화 안 됨
}
// C++17 이전: 재귀 종료용 특수화 함수가 별도 필요
}
print_all(1, "hello", 3.14);
template<typename Container>
void clear(Container& c) {
if constexpr (requires { c.clear(); }) {
c.clear(); // clear() 있는 컨테이너
} else {
c = Container{}; // 없으면 재할당
}
}

C++20 requires와 조합하면 더욱 강력합니다.

template<typename T>
auto make_default() {
if constexpr (std::is_same_v<T, std::string>) {
return std::string{};
} else if constexpr (std::is_arithmetic_v<T>) {
return T{0};
} else {
static_assert(false, "지원하지 않는 타입");
}
}

static_assert(false, ...)를 사용할 때는 템플릿 파라미터에 의존하는 형태로 작성해야 합니다.

// 올바른 방식
template<typename T>
struct always_false : std::false_type {};
if constexpr (...) { ... }
else { static_assert(always_false<T>::value, "지원하지 않는 타입"); }
  • if constexpr는 선택되지 않은 분기를 인스턴스화하지 않으므로 템플릿 코드가 훨씬 간결해짐
  • SFINAE, 태그 디스패치, 템플릿 특수화 등의 복잡한 패턴을 대체 가능
  • C++20 concepts와 함께 사용하면 제약 조건을 더 명확하게 표현 가능
  • 런타임 조건에는 사용할 수 없으며, 조건식은 반드시 constexpr 평가 가능해야 함