Constexpr in C++

Pages: 12
Thanks for the answer. I yet have two other questions. Hopefully you won't change your simple language! :)

If constexpr is for performance elevating, why not using it always? For instance, for all functions?! (We are allowed to utilise them for both const and variable arguments without defining another function, as stated in post #1).

And, doesn't applying constexpr for a function, for example, have lower authority for the compiler to evaluate the function at compile time than that new consteval? I comprehend this way!
Last edited on
If constexpr is for performance elevating, why not using it always? For instance, for all functions?!


It is a bit more specific to qualify that constexpr is able to improve performing by performing calculation at compile time. From a user's perspective executing the resulting application, nothing is faster than what was already done before they even started the application.

It is also a descriptive decoration, much like an adjective.

As such, it shouldn't be used everywhere that the description does not apply. A function which can't possibly be evaluated at compile time should not be declared constexpr on the hope somehow it might be at some point, or that the declaration as constexpr would be harmless if it were not. The human reader would, unless some careful reading were applied, assume that a constexpr declaration has at least some chance of being evaluated at compile time, and that it was the intent of the author to take advantage of that. If the underlying function can't possibly be constexpr, it would be like applying the adjective "happy" to everything being said, even when it didn't apply.

In some ways one might say that the constexpr keyword isn't even necessary from the viewpoint of optimization. Most good optimizers recognize when expressions happen to evaluate at compile time and avoid emitting run-time code in such situations anyway.

However, it's use allows the compiler to "recognize" the author's intent to rely upon some expression forms which are potentially evaluated a compile time, and write code that can make that assumption where appropriate. Optimizers themselves are far removed from what we write, and should be, but here we have a keyword which brings such a concept within reach of the code consuming a constexpr, giving rise to author controlled (or at least requested) compile time computation by intent.

In some ways this mimics the reality behind "inline", another "performance elevating" keyword, which has an otherwise not to well recognized meaning. The "inline" keyword is almost unnecessary from the viewpoint of optimization. It is often even ignored by the compiler, giving rise to frustration when this is recognized. However, the "inline" keyword is as much about informing the linker that duplicate functions are to be expected (and warnings not issued) because the function was written in a header "inline" and not explicitly compartmentalized into a translation unit (CPP file). That is it's primary purpose, not the hint that the emitted machine code should skip a function call.

The optimizer will "skip" function calls by inline code whenever it is possible (and configured to do so), with or without the "inline" keyword.

This is much the same with respect to your thinking about sprinkling "constexpr" everywhere - it really wouldn't elevate performance any more than good optimizers do anyway. Like the "inline" keyword informing the linker to ignore the duplicate appearances of functions in the build, the "constexpr" keyword is less about the optimized output and more about whether or not what we write can use an expression with compile time evaluation within the knowledge of the human author, and the features of the language - a stage far ahead of and before optimization of the final product.




A simplification:

constexpr (in the context of functions) says that it should be possible to use a function in a constant expression. Meaning, it should be possible to evaluate at compile time if you give it constants, and the compiler should complain if there's something in that function that prevents this. The C++ standard actually imposes a list of restrictions on what can appear in a constexpr function, a list that gets shorter and shorter with each version of C++.

Optimizing compilers will already evaluate a lot of stuff at compile time, even if that code isn't marked constexpr. Using constexpr is not mandatory to get the optimization, and using constexpr doesn't guarantee that you will get the optimization. It's just a way of constraining yourself to write code that the compiler can evaluate at compile time (and maybe providing a hint to the compiler that it should do so).

consteval forces the matter a bit further, saying that each call to a constval function must produce a constant expression (which means, among other things, that it can only be passed constant expressions). Someone should correct me if I'm wrong, but I believe the draft stops short of saying that expression absolutely has to be evaluated at compile-time (though any decent optimizing compiler will do it anyway for simpler expressions).

In summary:
constexpr: "can be used in a constant expression".
consteval: "must be made into a constant expression".

-Albatross
Last edited on
The standard doesn't really have a way to say that things should be evaluated at compile time because as long as the behaviour is correct the compiler can essentially do whatever it wants.

A call to a constexpr function in a context where it doesn't make a difference if the result is constexpr or not is essentially just a regular function call. The compiler might be more willing to try and evaluate the function at compile time if its marked constexpr but if that fails it will have to fall back to runtime evaluation. This has the potential to slow down compilation, because compile time evaluation is slow, which could explain why many compilers don't evaluate constexpr functions at compile time whenever possible.

If you call a constexpr function where the result has to be constexpr (e.g. as a non-type template argument, or to initialize a constexpr variable) there are much more rules that has to be fulfilled. The evaluation cannot invoke non-constexpr functions, it cannot contain undefined behaviour, etc. If these rules are broken it has to show a compilation error. The simplest way to validate that all of these rules are followed is to simply try and evaluate the expression at compile time. It would be stupid to throw away the result after it has been calculated (except perhaps in debug builds) so you can essentially rely on it being evaluated at compile time in these cases.

So to sum things up. Don't expect constexpr functions to be evaluated at compile time unless the result has to be constexpr. If you really want compile time evaluation for performance reasons you can store the result in a constexpr variable but be aware that this can lead to slower compilation times and there might also be implementation-defined limits on the number of recursions and expressions that can be evaluated.

It is possible that compilers will get more aggressive in evaluating constexpr functions in the future but C++20 will introduce a new function, std::is_constant_evaluated(), that will allow you to provide two implementations, one that is constexpr friendly, and another one that is faster at runtime but cannot be constexpr for some reason. As currently specified, std::is_constant_evaluated() is guaranteed to return false in most cases where the result doesn't have to be a constexpr which means that many constexpr functions that will use this technique will end up with code paths that is not valid constexpr, and therefore might be difficult for the compiler to evaluate at compile time, even though all arguments are constexpr. This is yet another reason to actually be explicit and assign the result to a constexpr variable if you want to make sure the result is evaluated at compile time because otherwise, for some functions, it will not be possible for the compiler to evaluate at compile time even if it wanted to.
Last edited on
> If constexpr is for performance elevating, why not using it always? For instance, for all functions?

Use constexpr liberally for constant variables; if its value can only be known at run-time use const instead.

Things area bit more involved for functions.

Non-inline functions can't be constexpr anyway. IMHO, the general guideline would be:

If a function is small enough, stable enough, and its performance is critical, implement it inline.

Make it constexpr only if it is a requirement (or in the case of a library, a desirable property) that compile-time evaluation of the function should be allowed.
To the extent I understood, my thought is that all constexpr, consteval and inline keywords can be very readily left out from explicitly writing in code and being overwhelmed by them toughly (at times). Good optimizers (which are inside good compilers) themselves can use the three well, implicitly, when needed, saving us hours getting bogged down in making sure they're used in the code properly and will lead to the expected result.

A VS 2019 compiler is the means that can be considered as being able to apply correct optimizations, especially those three ones, when needed.

Now, do you disagree, really?
Last edited on
frek wrote:
Now, do you disagree, really?
Yes. Not only is it (EDIT: focusing on constexpr here specifically, sorry about the ambiguity) a good way to keep another programmer (including future you) from making your code unable to be evaluated at compile time, but consider that some templates (std::array) can take constants as template parameters. Depending on the template (and what you need from it), being able to use certain functions in the computation of your template's non-type parameters could potentially be very useful.

-Albatross
Last edited on
If we ignore the performance implications:

inline - Multiple definitions of the same inline function in different translation units are allowed as long as all definitions are the same. This allows you to define functions in header files. This is why inline has been extended to variables in C++17. Inlining as an optimization does not make sense for variables but it's useful to be able to define global variables (including static members) in header files. Note that functions that are defined inside a class definition are implicitly inline.

constexpr - There are many places in the language where the value has to be a constant expression. E.g. template arguments, the size of non-dynamically allocated arrays, static_assert, etc. Constant expressions can also be used to avoid the "static initialization order fiasco". Traditionally constant expressions could only be simple expressions. No function calls were allowed. The introduction of constexpr allow you to have functions in constant expressions. Constexpr functions are implicitly inline.

consteval - Some functions don't make sense at runtime. I don't expect this to be something that will be heavily used by most people. The best motivating example is probably "static reflection". It's currently a work-in-progress but reflection will allow you to do things like looping over the members of a class and get information about them. This is not something you can do at runtime because the information that allow you to do these things are known only at compile time.
Last edited on
I read all messages once again and reached the prior conclusion. Based on what you said, roughly, almost all compile time evaluations can be accomplished by a good compiler and we can keep ourselves from the use of constexpr and inline explicitly.
If the conclusion is wrong, please offer some simple examples where explicitly using those three keywords in code will result in better performance and even good compilers may fail to reach that performance if we don't use the keywords explicitly.




If you reread the messages and reached the prior conclusion (i.e. that constexpr, along with inline and consteval, can be broadly omitted from code given to a modern optimizing compiler, please correct me if your conclusion was actually different), then I'm not sure there's anything I can say to convince you of reality, since I doubt I can get much clearer than
some templates (std::array) can take constants as template parameters

...to say nothing of the myriad examples of where those three are necessary given by Peter87.

To reiterate: compile-time evaluation is not merely an optimization feature.

-Albatross
Last edited on
Here's an example using a silly implementation of a string class from a recent thread. Examine the code generated by GCC and Clang under maximum optimization. Then un-comment line 7 and compare:
https://godbolt.org/z/YRKD70

The code comes from this thread:
http://www.cplusplus.com/forum/beginner/257332

Albatross wrote:
To reiterate: compile-time evaluation is not merely an optimization feature.
+1
Last edited on
Yeah, much littler computations. Good. :)

Now these prior answers: Duthomhas,
C and C++ compilers, at least, will do it for you so that no time is spent doing it when your executable runs.
The concept of when code is evaluated/executed/computed between the two are the same, though, so we can safely ignore the differences in this context.


Niccolo,
In some ways one might say that the constexpr keyword isn't even necessary from the viewpoint of optimization. Most good optimizers recognize when expressions happen to evaluate at compile time and avoid emitting run-time code in such situations anyway.
The "inline" keyword is almost unnecessary from the viewpoint of optimization.
This is much the same with respect to your thinking about sprinkling "constexpr" everywhere - it really wouldn't elevate performance any more than good optimizers do anyway.


Albatross,
Optimizing compilers will already evaluate a lot of stuff at compile time, even if that code isn't marked constexpr. Using constexpr is not mandatory to get the optimization, and using constexpr doesn't guarantee that you will get the optimization.


and Peter87
The compiler might be more willing to try and evaluate the function at compile time if its marked constexpr
as long as the behaviour is correct the compiler can essentially do whatever it wants.


By reading them I thought using those three keywords were not necessarily required to be written by the programmer, but instead, the good compiler can recognise where it's needed to evaluate variables, functions and etc at compile time. But now I think there are situations where even good compilers can't evaluate expressions at compile time if there's no constexpr keyword explicitly written by the programmer.

Before the (probably final) conclusion, let me ask this query: Is it clear where it's mandatory to write the keyword constexpr explicitly to have compile time evaluations? And if yes, in what situations?



Last edited on
Is it clear where it's mandatory to write the keyword constexpr explicitly to have compile time evaluations?

No. It isn't clear where the compiler optimizer would perform compile-time evaluation on its own (especially where constexpr is absent). But read carefully: compile-time evaluation is an optimization, irrelevant to the behavior of your program. The compiler optimizer never* affects the observable behavior of your program.

In contrast, constant evaluation is (roughly) the evaluation of a constant expression, which is formally defined. There are contexts (for example, in the bounds of an array) where constant expressions are required.

Consider the function f:
 
int f() { return 2; }
No matter how good the compiler optimizer is, the expression f() is not formally a constant expression, and therefore the expression f() can't be evaluated in array bounds. For example,
 
int x[f()];
will never compile.

To make f() a constant expression, declare it a constexpr function.
In other contexts, the compiler optimizer might change f() to 2 either way.

*never, with two unrelated exceptions.
Last edited on
frek wrote:
By reading them I thought using those three keywords were not necessarily required to be written by the programmer, but instead, the good compiler can recognise where it's needed to evaluate variables, functions and etc at compile time.

They can, but optimizations are always a tradeoff between compile time and runtime. C++ code is already slow to compile so it's not clear if it's always worth trying to evaluate as much as possible at compile time.

In the code that mbozzi linked to, if you uncomment line 7 and remove MAYBE_CONSTEXPR from line 82 you'll see that the two compilers end up doing different tradeoffs. https://godbolt.org/z/RVR__7

But now I think there are situations where even good compilers can't evaluate expressions at compile time if there's no constexpr keyword explicitly written by the programmer.

What inlining (as an optimization) and compile-time evaluation has in common is that the implementation has to be visible to the compiler when it generates the code. With traditional compilation, where each translation unit is compiled in separation, there is no way it could inline or evaluate functions that are not defined in the same translation unit. This is why you have to define inline and constexpr functions in headers.

Is it clear where it's mandatory to write the keyword constexpr explicitly to have compile time evaluations? And if yes, in what situations?

If you have a constant expression that you want to be evaluated at compile time, that is not used where a constant expression is required, just store the result in a constexpr variable.

 
constexpr int x = f();

The compiler would have to have a very good reason for not evaluating such an expression at compile time.
Last edited on
@mboozi:
I've read lots about a constant expression but to express it simply we can say: a constant expression is an expression which is evaluated at compile time and it's marked by either the const or constexpr keyword. Right?

@Peter87
This is why you have to define inline and constexpr functions in headers.

So 3 ways we are required to use these two are a) a constexpr function for constant expressions and variables to be used differently b) defining a constexpr expression for situations like the array bounds mboozi mentioned above and c) defining constexpr and inline in header files.

I didn't understand the explanation about your example in the last part above!
Last edited on
I've read lots about a constant expression but to express it simply we can say: a constant expression is an expression which is evaluated at compile time and it's marked by either the const or constexpr keyword. Right?

Replace "is" with "can be".

A constant expression is an expression which can be evaluated at compile time.

2 + 9 is an example of a constant expression.

std::max(4, 6) / 2 is another example of a constant expression (thanks to std::max being constexpr).

I didn't understand the explanation about your example in the last part above!

I mean, if f() is a constexpr function, and you use it in a context where a constant expression is not needed, e.g.

 
std::cout << f();

then you don't really care if f() is evaluated at compile time or not.

If you want f() to be evaluated at compile-time you should assign the returned value to a constexpr variable before using it, e.g.

1
2
constexpr int x = f();
std::cout << x;

In practice, this ensures that f() is evaluated at compile time.
Last edited on
Thank you very much.
I don't think I have another question in this case. I also appreciate your replies so much.
Topic archived. No new replies allowed.
Pages: 12