constexpr functions are functions that can be executed at compile time. Sounds not so thrilling. But it is. Trust me. You can perform with constexpr functions a lot of calculations at compile time. Therefore, the result of the calculation is at runtime as a constant in ROM available. In addition, constexpr functions are implicitly inline.
The syntax of constexpr functions was massively improved with the change from C++11 to C++14. The positive list from C++11 is with C++14 a negative list. In C++11 you had to keep in mind which feature you can use in a constexpr functions. With C++14 you only have to keep in mind which feature you can't use in a constexpr function.
C++11
For constexpr functions there are a few restrictions:
The function
The restriction goes on with the function body. The key points are that
 it has to be defined with the keyword default or delete or
 can only have one return statement.
Is it possible to define a meaningful function with such restrictions? Because constexpr functions can not have a conditional like if or a loop. Yes. Have a look at the constexpr functions in the post Constant expressions with constexpr. Only the functions getAverageDistance requires the C++14 standard.
Fortunately, we have the ternary operator and recursion in C++. Therefore, I can implement the gcd algorithm as a constexpr function.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

// constexpr11.cpp
#include <iostream>
constexpr int gcd(int a, int b){
return (b== 0) ? a : gcd(b, a % b);
}
int main(){
std::cout << std::endl;
constexpr int i= gcd(11, 121);
int a= 11;
int b= 121;
int j= gcd(a,b);
std::cout << "gcd(11,121): " << i << std::endl;
std::cout << "gcd(a,b): " << j << std::endl;
std::cout << std::endl;
}

I implemented in line 6 the Euclidean algorithm with the help of the ternary operator and recursion. Comprehensible code looks different. Of course, I can invoke the gcd function with arguments that are nonconstant expressions (line 15 and 16). Therefore, the result will be calculated at runtime and can only be taken by a nonconstant expression (line 17).
The result is a little bit boring. But wait a second.
A glimpse on the assembler instructions is quite enlightening.
The call of the constexpr function in line 13 causes that the result of gcd(11, 121) is available in the object file. This is opposite to the function call in line 17. At first, the processor has to push the variables on the stack (Instructions 400939  400941 in the object dump). At second, the processor has to invoke (callq) the function and store the result in the variable j (400948). What a difference!
Of course, you can define more powerful constexpr functions by calling ternary operators from ternary operators and so on. This technique is Turing complete. So you can compute all, that is computable. Fortunately, you don't have to do that. You can use the C++14 constexpr functions, which behave almost like ordinary functions.
C++14
constexpr can have
 conditional jump instructions or loop instructions.
 more than one instruction.
 constexpr functions.
 fundamental data types that have to be initialized with a constant expression.
The difference between ordinary functions to constexpr functions in C++14 is minimal. constexpr functions can not have static or thread_local data. Either can they have a try block nor a goto instruction? Therefore, it's quite easy to implement the gcd algorithm in C++14 as a constexpr function.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

// constexpr14.cpp
#include <iostream>
constexpr auto gcd(int a, int b){
while (b != 0){
auto t= b;
b= a % b;
a= t;
}
return a;
}
int main(){
std::cout << std::endl;
constexpr int i= gcd(11,121);
int a= 11;
int b= 121;
int j= gcd(a,b);
std::cout << "gcd(11,121): " << i << std::endl;
std::cout << "gcd(a,b): " << j << std::endl;
std::cout << std::endl;
}

I skip the output of the program because it's identical to the output of the C++11 variant.
Thanks to a discussion with Odin Holmes in the Facebook group of this blog Modernes C++ I want to present a very interesting use case for constexpr functions.
Pure functions
You can execute constexpr functions at runtime. If you take the return value of a constexpr function by a constant expression, the compiler will perform the function at compile time. The question is: Is there a reason to perform a constexpr function at runtime? Of course, you have the constexpr function so can use it at runtime. But there is a much more convincing reason.
A constexpr function can be potentially performed at compile time. There is no state at compile time. At compile time we are in a pure functional sublanguage of the imperative programming language C++. In particular, that means that at compile time executed functions have to be pure functions. When you use this constexpr function at runtime the function keeps pure. Pure functions are functions that return always the same result when given the same arguments. Pure functions are like infinitely large tables from which you get your value. The guarantee that an expression returns always the same result when given the same arguments is called referential transparency.
Pure functions have a lot of advantages:
 The function call can be replaced by the result.
 The execution of pure functions can automatically be distributed to other threads.
 The function call can be reordered.
 They can easily be refactored.
The last three points hold because pure functions have no state and have therefore no dependency on the environment. Pure functions are often called mathematical functions.
There are a lot of good reasons to use constexpr functions. The table shows the key points of pure and impure functions.
I will write in a few weeks about functional programming in C++. Then I will explain the details of pure functions and pure functional programming.
Pure and purer
I want to stress one point. constexpr functions are not per se pure. Thanks to Marcel Wid, who made me aware of this. constexpr functions are a lot purer than ordinary functions. E.g. you can only invoke a constexpr function in a constexpr function. Who wants only to use pure functions should study Haskell. The very well written introduction to Haskell Learn You a Haskell For Great Good from Miran Lipovaca is online available.
What's next?
In the post More and more save I successively optimized the gcd algorithm. Thanks to the typetraits library and static_assert this was quite impressive. But that was not the whole story about the typetraits. By analysing the type system of your code you can write selfoptimizing programs. How? Read the next post.
{tooltip} {endtexte} Go to Leanpub/cpplibrary "What every professional C++ programmer should know about the C++ standard library". {endtooltip} Get your ebook. Support my blog.
Comments
RSS feed for comments to this post