C++17coreadvanced
if constexpr
C++17 · Practical Modern · Compiler flag: -std=c++17
Explanation
Enables compile-time conditional evaluation inside templates. The discarded branch is not instantiated, eliminating the need for SFINAE or tag-dispatch tricks in many generic programming scenarios.
Real-World Scenario
Select different serialization logic paths in a generic function depending on whether the type is arithmetic or a string.
Quick Snippet
snippet.cpp
#include <type_traits>
#include <string>
template<typename T>
std::string serialize(const T& value) {
if constexpr (std::is_arithmetic_v<T>) {
return std::to_string(value);
} else {
return std::string(value);
}
}Full Example
if_constexpr.cpp
// CodeNebula - C++17 Example: if constexpr
// Compile: g++ -std=c++17 -Wall -Wextra -o example if_constexpr.cpp
// Demonstrates if constexpr for compile-time branching. The discarded branch
// is not instantiated, enabling template code that would otherwise fail to compile.
#include <iostream>
#include <string>
#include <type_traits>
// ------------------------------------------------------------------
// 1. Type-dependent formatting: only the matching branch is compiled
// ------------------------------------------------------------------
template <typename T>
std::string to_description(T value) {
if constexpr (std::is_integral_v<T>) {
return "integer(" + std::to_string(value) + ")";
} else if constexpr (std::is_floating_point_v<T>) {
return "float(" + std::to_string(value) + ")";
} else {
// T must be convertible to string
return "other(" + std::string(value) + ")";
}
}
// ------------------------------------------------------------------
// 2. Safe member access: check for pointer vs value
// ------------------------------------------------------------------
template <typename T>
auto get_value(T t) {
if constexpr (std::is_pointer_v<T>) {
return *t; // dereference pointer
} else {
return t; // return by value
}
}
// ------------------------------------------------------------------
// 3. Compile-time recursion with variadic templates
// ------------------------------------------------------------------
template <typename T, typename... Rest>
void print_all(T first, Rest... rest) {
std::cout << " " << first;
if constexpr (sizeof...(rest) > 0) {
std::cout << ",";
print_all(rest...); // only instantiated when rest is non-empty
} else {
std::cout << std::endl;
}
}
int main()
{
// ------------------------------------------------------------------
// Type-dependent formatting
// ------------------------------------------------------------------
std::cout << "=== to_description ===" << std::endl;
std::cout << " " << to_description(42) << std::endl;
std::cout << " " << to_description(3.14) << std::endl;
std::cout << " " << to_description("hello") << std::endl;
// ------------------------------------------------------------------
// Safe pointer/value access
// ------------------------------------------------------------------
int x = 99;
int* px = &x;
std::cout << "\n=== get_value ===" << std::endl;
std::cout << " from int: " << get_value(x) << std::endl;
std::cout << " from int*: " << get_value(px) << std::endl;
// ------------------------------------------------------------------
// Variadic print using compile-time recursion
// ------------------------------------------------------------------
std::cout << "\n=== print_all ===" << std::endl;
print_all(1, 2.5, "three", 'Z');
return 0;
}
/*
Expected output:
=== to_description ===
integer(42)
float(3.140000)
other(hello)
=== get_value ===
from int: 99
from int*: 99
=== print_all ===
1, 2.5, three, Z
*/
Run command
g++ -std=c++17 -Wall -Wextra -o example if_constexpr.cpp