T가 std::string일 때는 value.size()를 사용하는 branch가 선택됩니다.
T가 std::vector<int>일 때는 value.front()를 사용하는 branch가 선택됩니다.
중요한 점은 std::vector<int>를 std::cout에 직접 출력하는 branch가 instantiation되지 않는다는 것입니다.
5. 일반 if와 어떻게 다른가?
일반 if는 runtime branch입니다.
조건이 runtime에 결정되므로 compiler는 기본적으로 두 branch의 코드가 모두 type-correct해야 한다고 봅니다.
반면 if constexpr는 조건이 compile time에 결정되므로, template instantiation에서 선택되지 않은 branch를 버릴 수 있습니다.
이 차이는 타입별로 서로 다른 interface를 호출할 때 크게 드러납니다.
상태 변화
print_value(42) 호출
T = int 로 template instantiation
std::is_integral_v<T> 조건이 true
integral branch만 남음
std::string branch와 std::vector<int> branch는 discarded statement
컴파일된 함수는 int 출력 코드만 포함
이 흐름 덕분에 하나의 template 함수 안에 여러 타입 전용 처리를 모아둘 수 있습니다.
다만 조건 자체가 runtime 값이면 if constexpr를 사용할 수 없습니다.
예를 들어 사용자 입력, 파일 내용, network response처럼 실행 중에만 알 수 있는 값은 일반 if의 영역입니다.
6. 그림으로 이해하기
if constexpr는 template parameter가 정해진 뒤 compile-time 조건에 맞는 branch만 남기고, 선택되지 않은 branch를 해당 instantiation에서 제외합니다.
그림에서 중요한 지점은 branch 선택 시점입니다.
Runtime if는 program이 실행될 때 path가 갈라지지만, if constexpr는 compile time에 타입과 trait를 기준으로 path가 정리됩니다.
그래서 결과 binary에는 해당 instantiation에 필요한 코드만 남을 수 있습니다.
7. 자주 하는 오해
if constexpr는 일반 if보다 항상 빠른 if다. 핵심은 runtime 성능보다 compile-time branch입니다. 값이 runtime에 결정되는 상황에는 일반 if가 맞습니다.
선택되지 않은 branch에는 아무 코드나 넣어도 된다. 문법적으로 잘못된 코드는 여전히 문제가 될 수 있습니다. 또한 type-dependent expression인지 아닌지에 따라 compile error가 나는 시점이 달라집니다.
if constexpr를 쓰면 SFINAE가 필요 없다. 간단한 함수 내부 분기에는 if constexpr가 읽기 쉽지만, overload resolution 단계에서 후보 자체를 제거해야 하는 경우에는 SFINAE나 concepts가 더 적합할 수 있습니다.
type_traits를 쓰면 코드가 무조건 과해진다. 타입별 처리가 실제로 필요하다면 type_traits와 if constexpr의 조합은 의도를 명확히 드러냅니다. 문제는 필요한 조건보다 복잡한 trait를 억지로 만드는 경우입니다.
Unsupported type은 그냥 else에서 처리하면 충분하다. Library code라면 static_assert로 명확한 compile error를 내는 것이 더 좋을 수 있습니다. Debug helper라면 fallback 출력도 실용적입니다.
if constexpr는 template metaprogramming을 숨기는 도구가 아니라, 필요한 compile-time 결정을 함수 본문 안에 드러내는 도구입니다.
조건이 정말 compile-time 성격인지, 아니면 runtime state인지 먼저 구분해야 합니다.
8. 실무에서는 어떻게 볼까?
실무에서 if constexpr는 generic helper, serialization, logging, numeric utility, container utility에서 자주 유용합니다.
특히 타입별로 가능한 operation이 다를 때 branch를 함수 안에 모아둘 수 있어 코드가 읽기 쉬워집니다.
반대로 타입별 동작이 커지고 책임이 많아진다면 if constexpr chain이 길어지며 유지보수가 어려워질 수 있습니다.
상황
if constexpr 적합도
검토할 대안
함수 안에서 타입별로 짧게 처리
높음
type_traits 조합
Overload 후보 자체를 제한해야 함
중간
SFINAE, concepts
타입별 구현이 크고 독립적임
낮음
overload, specialization, policy class
runtime 설정에 따라 분기
낮음
일반 if, strategy object
코드 리뷰에서는 if constexpr가 들어간 조건이 type-level decision인지 확인하는 것이 중요합니다.
또한 branch가 많아진다면 “이 함수가 너무 많은 타입 정책을 알고 있는가”를 점검해야 합니다.
작은 helper라면 if constexpr가 깔끔하지만, domain logic이 branch마다 커진다면 overload나 별도 type으로 분리하는 편이 더 낫습니다.
9. 정리
if constexpr는 C++17의 compile-time branch 문법입니다.
Template instantiation에서 조건이 결정되면 선택되지 않은 branch는 discarded statement로 처리됩니다.
타입마다 가능한 operation이 다를 때 SFINAE보다 읽기 쉬운 함수 내부 분기를 만들 수 있습니다.
일반 if를 대체하는 성능 문법이 아니라 compile-time decision을 표현하는 문법으로 봐야 합니다.
Branch가 길어지면 overload, specialization, concepts 같은 다른 설계를 검토해야 합니다.
if constexpr를 잘 쓰는 기준은 “실행 중 상태”가 아니라 “타입이 정해지면 함께 정해지는 코드 경로”를 분기하는 것입니다.