Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between "if constexpr()" Vs "if()"

What is the difference between if constexpr() and if()?

Where and When can I use both of them?

like image 920
msc Avatar asked Apr 16 '17 06:04

msc


People also ask

What does if constexpr do?

Constexpr ifIf the value is true, then statement-false is discarded (if present), otherwise, statement-true is discarded.

Why should I use constexpr?

A constexpr integral value can be used wherever a const integer is required, such as in template arguments and array declarations. And when a value is computed at compile time instead of run time, it helps your program run faster and use less memory.

Does constexpr improve performance?

In Conclusion. constexpr is an effective tool for ensuring compile-time evaluation of function calls, objects and variables. Compile-time evaluation of expressions often leads to more efficient code and enables the compiler to store the result in the system's ROM.

When to use #define vs constexpr?

#define (also called a 'macro') is simply a text substitution that happens during preprocessor phase, before the actual compiler. And it is obviously not typed. constexpr on the other hand, happens during actual parsing. And it is indeed typed.


1 Answers

The only difference is that if constexpr is evaluated at compile time, whereas if is not. This means that branches can be rejected at compile time, and thus will never get compiled.


Imagine you have a function, length, that returns the length of a number, or the length of a type that has a .length() function. You can't do it in one function, the compiler will complain:

template<typename T> auto length(const T& value) noexcept {     if (std::integral<T>::value) { // is number         return value;     else         return value.length(); }  int main() noexcept {     int a = 5;     std::string b = "foo";      std::cout << length(a) << ' ' << length(b) << '\n'; // doesn't compile } 

Error message:

main.cpp: In instantiation of 'auto length(const T&) [with T = int]': main.cpp:16:26:   required from here main.cpp:9:16: error: request for member 'length' in 'val', which is of non-class type 'const int'      return val.length();             ~~~~^~~~~~ 

That's because when the compiler instantiates length, the function will look like this:

auto length(const int& value) noexcept {     if (std::is_integral<int>::value) { // is number         return value;     else         return value.length(); } 

value is an int, and as such doesn't have a length member function, and so the compiler complains. The compiler can't see that statement will never be reached for an int, but it doesn't matter, as the compiler can't guarantee that.

Now you can either specialize length, but for a lot of types (like in this case - every number and class with a length member function), this results in a lot of duplicated code. SFINAE is also a solution, but it requires multiple function definitions, which makes the code a lot longer than it needs to be compared to the below.

Using if constexpr instead of if means that the branch (std::is_integral<T>::value) will get evaluated at compile time, and if it is true then every other branch (else if and else) gets discarded. If it is false, the next branch is checked (here else), and if it is true, discard every other branch, and so on...

template<typename T> auto length(const T& value) noexcept {     if constexpr (std::integral<T>::value) { // is number         return value;     else         return value.length(); } 

Now, when the compiler will instantiate length, it will look like this:

int length(const int& value) noexcept {     //if (std::is_integral<int>::value) { this branch is taken         return value;     //else                           discarded     //    return value.length();     discarded }  std::size_t length(const std::string& value) noexcept {     //if (std::is_integral<int>::value) { discarded     //    return value;                   discarded     //else                           this branch is taken         return value.length(); } 

And so those 2 overloads are valid, and the code will compile successfully.

like image 63
Rakete1111 Avatar answered Sep 28 '22 22:09

Rakete1111