C++ `if constexpr` | Compile-Time Branching in Templates
이 글의 핵심
Use if constexpr to discard untaken branches during instantiation—unlike runtime if, avoiding ill-formed code in unused branches for templates.
What is if constexpr?
Compile-time branching for templates (C++17). Discarded branches are not instantiated.
template<typename T>
auto getValue(T value) {
if constexpr (std::is_pointer_v<T>) {
return *value; // Only compiled when T is pointer
} else {
return value;
}
}
int x = 10;
auto a = getValue(x); // Uses else branch
auto b = getValue(&x); // Uses if branch
Basic usage
#include <type_traits>
#include <iostream>
template<typename T>
void print(T value) {
if constexpr (std::is_integral_v<T>) {
std::cout << "Integer: " << value << "\n";
} else if constexpr (std::is_floating_point_v<T>) {
std::cout << "Float: " << value << "\n";
} else {
std::cout << "Other: " << value << "\n";
}
}
// Usage
print(42); // "Integer: 42"
print(3.14); // "Float: 3.14"
print("hello"); // "Other: hello"
if vs if constexpr
Runtime if: All branches must compile
template<typename T>
void processRuntime(T value) {
if (std::is_pointer_v<T>) {
std::cout << *value; // ❌ Error when T=int: cannot dereference int
} else {
std::cout << value;
}
}
if constexpr: Only selected branch compiled
template<typename T>
void processCompileTime(T value) {
if constexpr (std::is_pointer_v<T>) {
std::cout << *value; // ✅ OK: only compiled when T is pointer
} else {
std::cout << value;
}
}
Real-world examples
1. Generic serialization
#include <type_traits>
#include <string>
#include <sstream>
template<typename T>
std::string toString(const T& value) {
if constexpr (std::is_arithmetic_v<T>) {
return std::to_string(value);
} else if constexpr (std::is_same_v<T, std::string>) {
return value;
} else if constexpr (std::is_same_v<T, const char*>) {
return std::string(value);
} else {
std::ostringstream oss;
oss << value;
return oss.str();
}
}
// Usage
auto s1 = toString(42); // "42"
auto s2 = toString(3.14); // "3.140000"
auto s3 = toString("hello"); // "hello"
2. Container optimization
template<typename Container, typename T>
void addElement(Container& c, const T& value) {
// Reserve space if container supports it
if constexpr (requires { c.reserve(1); }) {
c.reserve(c.size() + 1);
}
// Use push_back if available, otherwise insert
if constexpr (requires { c.push_back(value); }) {
c.push_back(value);
} else {
c.insert(c.end(), value);
}
}
// Works with vector (has reserve + push_back)
std::vector<int> vec;
addElement(vec, 10);
// Works with set (has insert only)
std::set<int> s;
addElement(s, 10);
3. Variadic print with recursion
template<typename T>
void print(const T& value) {
std::cout << value << "\n";
}
template<typename T, typename... Rest>
void print(const T& first, const Rest&... rest) {
std::cout << first;
if constexpr (sizeof...(rest) > 0) {
std::cout << ", ";
print(rest...); // Recursive call
} else {
std::cout << "\n";
}
}
// Usage
print(1, 2, 3, "hello", 3.14);
// Output: 1, 2, 3, hello, 3.14
Replacing SFINAE
Before C++17: SFINAE with enable_if
// Overload 1: for integral types
template<typename T>
std::enable_if_t<std::is_integral_v<T>, T>
square(T x) {
return x * x;
}
// Overload 2: for floating-point types
template<typename T>
std::enable_if_t<std::is_floating_point_v<T>, T>
square(T x) {
return x * x;
}
After C++17: Single function with if constexpr
template<typename T>
T square(T x) {
if constexpr (std::is_integral_v<T>) {
return x * x;
} else if constexpr (std::is_floating_point_v<T>) {
return x * x;
} else {
static_assert(std::is_arithmetic_v<T>, "T must be arithmetic");
}
}
Common mistakes
Mistake 1: Using runtime condition
template<typename T>
void process(T value, bool flag) {
if constexpr (flag) { // ❌ Error: flag is not constant expression
// ...
}
}
// ✅ Fix: use runtime if
if (flag) {
// ...
}
Mistake 2: Expecting different return types
template<typename T>
auto getValue(T x) {
if constexpr (std::is_pointer_v<T>) {
return *x; // Returns T's pointee type
} else {
return &x; // Returns T*
}
}
// ❌ Error: inconsistent return types
Fix: Use common return type or std::variant:
template<typename T>
auto getValue(T x) -> std::conditional_t<std::is_pointer_v<T>,
std::remove_pointer_t<T>,
T> {
if constexpr (std::is_pointer_v<T>) {
return *x;
} else {
return x;
}
}
Mistake 3: Assuming complete elimination
template<typename T>
void debug(T value) {
if constexpr (false) {
value.nonExistentMethod(); // ⚠️ May still cause error in some contexts
}
}
Note: Discarded branches still undergo phase 1 parsing. They must be syntactically valid.
Performance implications
Zero runtime cost: if constexpr is resolved at compile time. Generated code contains only the selected branch.
Assembly comparison (GCC 13, -O3):
template<typename T>
int process(T x) {
if constexpr (std::is_integral_v<T>) {
return x * 2;
} else {
return static_cast<int>(x);
}
}
int a = process(10); // Assembly: imul eax, 2
int b = process(3.14); // Assembly: cvttsd2si eax, xmm0
No branching instructions—completely different code paths.
Compiler support
| Compiler | if constexpr | Notes |
|---|---|---|
| GCC | 7+ | Full support |
| Clang | 3.9+ | Full support |
| MSVC | 2017 15.3+ | Full support |
Related posts
Keywords
C++, if constexpr, C++17, compile-time, templates, metaprogramming, SFINAE alternative
Frequently Asked Questions (FAQ)
Q. When would I use this in practice?
A. Use if constexpr to discard untaken branches during instantiation—unlike runtime if, avoiding ill-formed code in unu…
Q. What should I read before this?
A. Follow the previous article or related articles links at the bottom of each post to learn in sequence. See the C++ series index for the full picture.
Q. Where can I study this more deeply?
A. Check cppreference and the relevant library’s official documentation. The reference links at the end of the article are also worth using.
Related Articles (Internal Links)
Other articles related to this topic.
- C++ constexpr 함수 | ‘컴파일 타임 함수’ 가이드
- C++ constexpr Lambda | ‘컴파일 타임 람다’ 가이드
- C++ CTAD | ‘클래스 템플릿 인자 추론’ 가이드
Keywords Covered in This Article (Related Search Terms)
This article covers C++, if constexpr, C++17, Compile-time, Templates.