How it works...

Sometimes, we wish to change the behavior of our programs but the code that we are creating is always constant, meaning the compiler is capable of determining the value of the branch itself, as shown in this example:

if (!NDEBUG) {}

This is a common if statement used in a lot of code, including the standard library. If debugging is enabled, this code evaluates to true. We use this by adding debug statements to our code, which can be turned off. The compiler is smart enough to see that NDEBUG is true or false and will either add the code or remove the code completely. In other words, the compiler can make a simple optimization and reduce the size of the code as well as remove an unneeded branch as it knows the value of this if statement will never change at runtime. The problem is, this trick relies on the fact that the compiler is smart. The removal of the logic is implicitly trusted, which often leads to assumptions about what the compiler is doing. C++17 added a constexpr if statement that allows us to be explicit instead. It allows us to tell the compiler: that the statement I am providing should be evaluated at compile time, not at runtime. What makes this truly powerful is that we get compile-time errors when this assumption is not true, meaning the optimizations we were implicitly trusting the compiler to perform, we can now verify at compile time, and if the assumptions are false, we are told so that we can fix the issue, as shown in this example:

#include <iostream>

constexpr auto answer = 42;

int main(void)
{
if constexpr (answer == 42) {
std::cout << "The answer is: " << answer << ' ';
}
else {
std::cout << "The answer is not: " << answer << ' ';
}

return 0;
}

The output is as follows:

In the preceding example, we create constexpr and evaluate it at compile time instead of runtime. If we change constexpr to an actual variable, constexpr if will result in the following error:

We can then use this in our template functions to change the behavior of our template functions based on the type that we are given, as shown in this example:

#include <iostream>
#include <iomanip>

template<typename T>
constexpr void foo(T &&t)
{
if constexpr (std::is_floating_point_v<T>) {
std::cout << std::setprecision(10);
}

std::cout << "The answer is: " << std::forward<T>(t) << ' ';
}

int main(void)
{
foo(42);
foo(42.12345678);
return 0;
}

In the preceding example, we use the std::is_floating_point_v type trait to determine whether the type that we were provided is a floating point or not. If the type is not a floating point, this will return constexpr false, which the compiler can optimize out. Since we are using constexpr if, we can ensure that our if statement is actually constexpr and not a runtime conditional.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset