Hello again!
In this video, we are going to look at constexpr functions.
In C++11, we can say that a function is constexpr.
This means that the arguments are constant expressions, and the return value is also a constant expression.
These constexpr functions are executed at compile time.
This uses an interpreter which runs inside the compiler.
So this is something which understands most of the C++ language.
So you can write this code in normal C++, and you get normal C++ error messages.
To do this, we put the constexpr key word before the function declaration.
So this function miles_to_km() is constexpr. A constexpr function must be what is known as "pure".
So this means it does not modify the arguments. And also, it does not modify any global or static variables.
So a pure function is not allowed to change anything outside the function body.
constexpr functions are in line, by definition. Because the function call is omitted.
And this also means we can have multiple definitions, so we can include a constexpr function definition
in a header. And we can include that header in as many source files as we like. And we are not going
to get any
errors about violating the "one definition rule".
So this means you can use constexpr functions in header-only libraries. And this is actually a very
common practice now, with C++, to distribute libraries as headers only.
So you can just include these into your program, and you do not need to worry about compiling source files
and linking them into your binary.
It is also possible to call a constexpr function with arguments which are not constant expressions. And
that means that the return value will not be a constant expression.
If you are using this in a situation where the return value is supposed to be a constant expression,
then you get a compiler ever.
But if it is not, then the function will be evaluated at runtime.
So that will work just like any other function in C++.
And the reason for allowing this, is so you can have a function which does the same thing for constant
expressions and variables, without having to write two almost identical versions.
So here is our constexpr function, miles_to_km().
So we can call this with a constant expression as argument.
The return value will be a constant expression.
We can call it with an argument, which is a variable. So not a constant expression, because it
can be modified. And the return value will not be a constant expression.
And this is going to be evaluated at runtime.
I have put a dummy main() function in here, just so we do not get any annoying errors about not having a
main() function.
So there we are. That compiles.
However, if we have a... If we call this function with an argument which is not a constant expression,
and the return value is supposed to be a constant expression.
So we are using this to initialize a constexpr variable. Which has to be initialized from a constant expression
Then this is going to give a compiler error.
So let's look at that. "Expression
"did not evaluate to a constant.
"Failure was caused by non-constant argument... See usage of 'arg'".
So that is an extremely clear error message.
It tells you what the problem is, where it is, and why there is an error. So, ten out of ten to Microsoft
for that! Just imagine what that would have been, if this was a template function!
constexpr functions are useful for performing calculations at compile time.
So this means the calculation is only done once. Or, at least, only once for every time you compile the
program. As opposed to every time that the program is run, by every user, on every site where it is installed.
There is no runtime overhead.
It takes no time at all to do this calculation, because it has been done before the program starts. And this
means your program will use less electricity.
In C++11, the rules were very limiting.
You could only have a single return statement in the body of a constexpr function.
So that is similar to the C++11 rules for lambda expressions. In C++14,
these were relaxed quite a bit. As they were for lambda expressions. And now you can have multiple statements
in the body of a constexpr function.
You can also declare local variables in the function body, provided they are initialized, and not static.
And you can use flow control.
You can have if statements, switch statements, and loops. Including rang-for loops.
There are still restrictions on side-effects.
The body of a constexpr function may not contain any code which could cause an action at run-time.
Which includes memory management.
So that means you cannot use things like string and vector; calling virtual functions, and throwing
exceptions.
You can also have member functions which are constexpr.
So these take constant expressions as arguments, and return a constant expression.
This is different from having a member function, which is const. A const member function
means you cannot modify the object it was called on.
So the
"this" pointer is a pointer to const.
In C++11.
If you have a member function which is constexpr, then it was also const.
So these functions could not modify the objects they were called on.
In C++14, they are no longer const.
So you can modify the object.
If you want to prevent that, then you can declare them as const additionally. So you can have a member function which
is constexpr and const.
And finally, we can also have members of classes and structs which are constexpr.
So these are initialized from a constant expression, and they cannot be modified.
This is only possible if you declare the member as static. Which means it is not associated with any particular
object. Which means it is available at compile-time.
If you have a member which is not static, then it has to belong to a particular object.
And you can only have objects at run-time, because you need to call the constructor.
So that is why you have to declare them as static.
Okay, so that is it for this video.
I will see you next time.
But until then, keep coding!