C++ 17 – constexpr if condition
Let us say you have a conditional statement you can use in the if condition. And it can be evaluated at compile time, for example, as below.
std::is_integral_v<T>
And then, you use constexpr if instead of ordinary if, to evaluate the condition as well as the branch at compile time itself. See the example below.
if constexpr (std::is_integral_v<T>) { // this will compile as it is constant expression
return x * x;
} else {
return x;
}
In the above example, because the condition “std::is_integral_v<T>” can be evaluated at compile time, you can use if constexpr. If the condition is true, i.e., if T is one of integral data types such as int, then it would evaluate the condition at compile time. Further, if the condition is true, it will evaluate the branch under the true condition to the extent possible and keep it in executable code.
The else part would be left for evaluation at run time.
Let us say we have data type and value of x available at compile time.
int x = 5;
In this case, the “if constexpr” will convert to code equivalent to the below after compilation itself.
return 25;
If the value of x is not known, and we only have the data type of x at compile time. And the value of x is going to come only at run time.
int x;
In this case, “if constexpr” converts the code to equivalent code as below, at compile time itself.
return x * x;
If it is a condition that cannot be evaluated at compile time, it will act as a normal if condition.
If there are multiple conditions such as “if constexpr”, else-if, else, and if the conditions can be evaluated at compile time, it would evaluate the branches at compile time.
Below is a complete program demonstrating the usage of constexpr if. The code can be downloaded from GitHub at https://github.com/codeversionmaster/cplusplus/blob/cplusplus17/constexprif.cpp.
$ cat constexprif.cpp
/* Program to demonstrate constexpr if usage */
#include <iostream>
using namespace std;
template <typename T>
constexpr auto intdensify(T x) {
// if constexpr (std::is_integral_v<T> && x == 0) { // will not compile
if constexpr (std::is_integral_v<T>) { // this will compile as it is constant expression
return x * x;
} else {
return x;
}
}
int main() {
int x;
std::cout << "Enter x" << endl;
std::cin >> x;
// value of x is known at runtime, so code converts to return x * x at compile time
std::cout << intdensify(x) << std::endl;
// keeps 16 at compile time itself and prints 16
std::cout << intdensify(4) << std::endl;
// prints 1.5 and evaluates only at run time
std::cout << intdensify(1.5) << std::endl;
return 0;
}
The output is as below.
$ g++ -std=c++17 constexprif.cpp -o constexprif
ubuntu@ip-172-31-0-61:~/cplusplus$ ./constexprif
Enter x
2
4
16
1.5
This part of the code shows that the data type is known at compile time itself. Hence condition can be evaluated. However, the value of x is going to come at runtime only.
int x;
std::cout << "Enter x" << endl;
std::cin >> x;
// value of x is known at runtime, so code converts to return x * x at compile time
std::cout << intdensify(x) << std::endl;
// keeps 16 at compile time itself and prints 16
In this case, the compiler can convert the entire if else condition to return “x * x”.
This part of the code has everything available at compile time itself.
// keeps 16 at compile time itself and prints 16
std::cout << intdensify(4) << std::endl;
// prints 1.5 and evaluates only at run time
std::cout << intdensify(1.5) << std::endl;
So, based on if the condition is true or false, the compiler can keep “return 4” or “return 1.5” in the executable code itself even before runtime.
Advantages of “if constexpr”
The main advantages of “if constexpr” are as below.
- When the if condition and certain branches can be evaluated at compile time, that burden is removed from runtime execution. Hence, we see a performance improvement.
- Template metaprogramming is a C++ feature that involves computations using the template class. “if constexpr” is often used in such programming to reduce code duplication and improve performance.