C/C++ Arena

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

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).

Previous: constexpr Next: Values as template parameters