1. Problem It Solves
A template may need different implementations for different type categories. A normal if still requires both branches to be well formed after instantiation, even when one can never execute.
This lesson reduces that broad problem to one fixed-input program so the language rule and its observable result can be checked independently.
2. Prerequisites
A C++17 compiler invoked with warnings enabled and the earlier lessons listed in the course order.
Know function templates, type traits, discarded statements, and compile-time conditions.
3. Core Idea
if constexpr evaluates a constant condition during instantiation and discards the non-selected branch. Dependent invalid code in that discarded branch need not be instantiated for the chosen type.
Keep the type, object lifetime, ownership, and evaluation boundary visible while reading the example; syntax is useful only when those semantics are understood.
4. Minimal Syntax
if constexpr (std::is_integral_v<T>) {
return value + 1;
} else {
return value.size();
}5. How It Works
A template classifies arithmetic and string values with type-specific expressions.
Only the selected branch contributes code for each specialization, so string-only operations are never formed for integers.
The program prints descriptions for an integer, a floating-point value, and a string, giving a small test oracle that can be compared with the prediction made before compilation.
6. Common Mistakes
Non-dependent invalid syntax can still be diagnosed even in a discarded branch;
if constexpris not a general comment-out mechanism.A successful build is not proof of correct semantics. Recheck lifetimes, invalidation, ordering, error paths, and required headers or link flags for the real program.
7. When to Use It
Use this technique when one generic interface has genuinely different compile-time implementations by type capability or category.
Choose a simpler C++11/14 form when the C++17 rule does not improve safety, clarity, or measured performance for the supported toolchains.
8. Simple Example
The classifier uses only valid operations for each supplied type and returns ordinary strings that make branch selection visible.
The companion .cpp file has no input or external dependency. Predict the complete output, compile it, run it, then change one constant and explain the new result.
Complete sample code
Source file
cpp17/18_if_constexpr/main.cpp
#include <iostream>
#include <string>
#include <type_traits>
template <class T>
std::string describe(const 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 "floating " + std::to_string(value);
} else {
return "text length " + std::to_string(value.size());
}
}
int main() {
std::cout << describe(7) << '\n';
std::cout << describe(2.5) << '\n';
std::cout << describe(std::string{"C++17"}) << '\n';
}
9. Key Takeaways
if constexprselects template code during instantiation; it does not add runtime polymorphism.C++17 mode must be selected explicitly; a newer compiler default can otherwise hide a portability error.
Warnings, deterministic examples, and small assertions turn a remembered rule into evidence.
Document any lifetime, ownership, synchronization, or allocation contract at the API boundary.
10. Self-Check Questions
Easy — What problem does if constexpr address?
Medium — Which branch is instantiated for
describe(3.5)?Hard — Why can some errors still appear in a branch that is discarded for every current call?