Step 5 of 8
if constexpr
Sometimes a template needs to do slightly different things for different kinds of types. A normal if can't do that: both branches must compile for every T, even the branch that never runs. if constexpr decides at compile time, and the discarded branch isn't compiled for that type at all.
Combine it with type traits from <type_traits>, which answer questions about types at compile time:
| Trait | True for |
|---|---|
std::is_integral_v<T> |
int, long, char, bool, ... |
std::is_floating_point_v<T> |
float, double |
std::is_arithmetic_v<T> |
either of the above |
std::is_same_v<T, U> |
exactly the same type |
#include <iostream>
#include <string>
#include <type_traits>
template <typename T>
std::string describe(const T& v) {
if constexpr (std::is_same_v<T, bool>) {
return v ? "yes" : "no";
} else if constexpr (std::is_integral_v<T>) {
return "int " + std::to_string(v);
} else if constexpr (std::is_floating_point_v<T>) {
return "real " + std::to_string(v);
} else {
return "text '" + v + "'";
}
}
int main() {
std::cout << describe(true) << "\n";
std::cout << describe(42) << "\n";
std::cout << describe(0.5) << "\n";
std::cout << describe(std::string("hi")) << "\n";
}
yes
int 42
real 0.500000
text 'hi'
Why the plain if fails
With T = int, the last branch "text '" + v + "'" would be const char* plus int, which is nonsense and won't compile. if constexpr throws that branch away for int, so it's never compiled. With T = std::string, the std::to_string(v) branches are thrown away instead.
Notes
- The
boolcheck comes first becauseboolalso counts as integral. std::to_string(0.5)gives"0.500000"(six decimals), likeprintf("%f").- The
_vsuffix is shorthand:std::is_integral_v<T>meansstd::is_integral<T>::value.
Your turn: write template <typename T> std::string to_text(const T& v): for arithmetic types (std::is_arithmetic_v<T>) return std::to_string(v), and otherwise assume it's already a string and return "\"" + v + "\"" (quoted).