When does a constexpr function get evaluated at compile time? -- StackOverflow

Here's a common question about constexpr...

A suggestion: As of this writing the more correct and useful (and simpler) answer K-ballo's, which was not selected as best -- please upvote K-ballo and help approve the pending edit that improves it. Thanks.

When does a constexpr function get evaluated at compile time?

Since it is possible that a function declared as constexpr can be called during run-time, under which criteria does the compiler decide whether to compute it at compile-time or during runtime?

 

template<typename base_t, typename expo_t>
constexpr base_t POW(base_t base, expo_t expo)
{
    return (expo != 0 )? base * POW(base, expo -1) : 1;
}

int main(int argc, char** argv)
{
    int i = 0;
    std::cin >> i;
    std::cout << POW(i, 2) << std::endl;
    return 0;
}

 

In this case, i is unknown at compile-time, which is probably the reason why the compiler treats POW() as a regular function which is called at runtime. This dynamic however, as convenient as it may appear to be, has some impractical implications. For instance, could there be a case where I would like the compiler to compute a constexpr function during compile-time, where the compiler decides to treat it as a normal function instead, when it would have worked during compile-time as well? Are there any known common pitfalls?

Add a Comment

Comments are closed.

Comments (16)

0 0

K-ballo said on Jan 11, 2013 12:38 PM:

Thanks Herb, I already approved your edit.
0 0

Bjarne Stroustrup said on Jan 11, 2013 07:26 PM:

A constexpr function is evaluated at compile time if all its arguments are constant expressions.
0 0

chico said on Jan 13, 2013 06:28 PM:

ok, now we have B and H not agreeing how/when it should eval. confused.
0 0

Cedric said on Jan 14, 2013 02:07 AM:

This is all about optimization.
If the compiler is able to propagate constants up to the point where all arguments are constants, then the constexpr function will be evaluated at compile time.
1 0

H Sutter said on Jan 14, 2013 03:48 PM:

@chico: I didn't write the SO answer (I just edited it), but I'm not sure how it is different from Bjarne's one-line summary.

If you're thinking of the "when used in a constexpr context" part of the SO answer, note that the as-if rule applies -- if the program can't tell the difference, the compiler can transform your code to do work at compile time or defer it to run time. That's always been true regardless of whether constexpr is in the mix; for example, given "static const int i = 21+21;", a compiler is free to evaluate 21+21 at compile time and even allocate the variable in ROM if it wants to, or to evaluate it at run time and allocate the variable in RAM.

So for example:

constexpr int f( int i ) { return i; }

int arr[ f(42) ]; // A: must be evaluated at compile time

auto x = f(42) ; // B: compile time or run time

int main() {
auto y = f(42); // C: compile time or run time
}


For line A, the function must be evaluated at compile time because otherwise the program can't compile. For line B, because the program can’t tell the difference, the as-if rule applies and it could be done at either compile time or run time.

I just tried gcc 4.7 on gcc.godbolt.org, and with default optimization it does B at compile time and C at run time. Both are legal.
Here's the wording from the standard, 5.19 (Constant expressions), paragraphs 1 and 5, and the notes are non-normative but clearly document the above intent:

1. Certain contexts require expressions that satisfy additional requirements as detailed in this sub-clause; other contexts have different semantics depending on whether or not an expression satisfies these requirements. Expressions that satisfy these requirements are called constant expressions. [ Note: Constant expressions
can be evaluated during translation.— end note ] ...

...

5 [ Note: Although in some contexts constant expressions must be evaluated during program translation, others may be evaluated during program execution. Since this International Standard imposes no restrictions on the accuracy of floating-point perations, it is unspecified whether the evaluation of a floating-point expression during translation yields the same result as the evaluation of the same expression, or the same operations on the same values, during program execution. [Example:

bool f() {
char array[1 + int(1 + 0.2 - 0.1 - 0.1)]; // Must be evaluated during translation
int size = 1 + int(1 + 0.2 - 0.1 - 0.1); // May be evaluated at runtime
return sizeof(array) == size;
}


(I wrote my original example above before seeing this one, but the two examples are basically the same.)

Perhaps Bjarne's could say "can be" instead of "is" but that's about it, the intent is the same and cases where there is a difference can't matter to a valid program because a valid program can't tell the difference.
0 0

chico said on Jan 14, 2013 05:37 PM:

@Herb Sutter, thanks for the reply but I feel (maybe wrongly) that many programmers expect constexpr also as a tool for translation time calculations (note that I'm not talking about whether this is good or bad). With that purpose the difference between "is" and "may be" is critical. Such a programmer may want some calculation to happen at translation time only, he may leverage from tricks to remove the "may be" / "can't tell the difference" from the equation, or, if the standard provides support through a good tool for this job, he may use it instead.

constexpr is dubbed such a tool, if this tool assures it is evaluated at translation time given a number of rules and restrictions, the programmer will employ such a rules and restrictions to achieve his intent. So for him, knowing if it matters or not matters whether "the return value is used in [...]" affects his intent. He must know it, and it makes a huge difference for his purpose.

If the tool can't help him much to achieve what he wants, the tool does a poor job on that specific purpose.
0 0

H Sutter said on Jan 14, 2013 05:50 PM:

@chico: But programmers *can* rely on constexpr being fully compile-time in the cases where it matters -- to do compile-time things like initialize array sizes or instantiate templates. If they try to invoke something that isn't compile-time, the compiler will let them know.

In all other cases (using constexpr in places the language doesn't require to be compile-time), it doesn't matter. Programmers don't need to rely on anything else being done at compile vs. run time, and can't anyway because of optimizations -- the point of optimization is that the compiler can move work around, including in either direction from/to compile time to/from run time, and all such optimizations are legal as long as a well-formed program can't tell the difference (have different output) -- so it doesn't matter because the program is indistinguishable.

I could imagine someone may want to force a variable to be written to ROM (say), but that (a) is rarely necessary or interesting in practice (I can't remember if I've seen a true need for it myself), (b) is handled using compiler- and platform-specific extensions if at all (e.g., optimization levels and related controls), and (c) isn't specific to constexpr (e.g., it has always been the case that a compiler "might" put a static const int variable into ROM, and that's often cited as the canonical reason why casting away const is undefined behavior -- it may not be physically possible to mutate the const object).
0 0

K-ballo said on Jan 14, 2013 06:18 PM:

@Herb Sutter: The general expectation seems to be that constexpr functions are always evaluated at compile time. Cedric's comment is a clear example of that, he says "This is all about optimization". And hence expect that constexpr functions are always evaluated at compile time, regardless of its context. To me, they are all about expressiveness...

And regarding that whether something may or may not be evaluated at compile time is "indistinguishable" for a well defined program, that's a very subtle point... Consider a simple recursive definition of factorial, whether the evaluation is done at compile time or runtime is an observable difference. A program that does different things based on how long the evaluation lasts could certainly produce different outputs (far fetched, agreed). But most notably, factorial(512) is guaranteed to work at compile time while there are no guarantees it won't result in a stack overflow if evaluated at runtime.

The arbitrary subset of allowed operations together with the context and implementation dependent nature of compile time versus runtime evaluation prevents constexpr functions from being "all about optimization" for the majority of cases. I think the emphasis of constexpr should be put in their expressiveness and not in the potential opportunity for optimizations. Maybe I'm wrong here, but if I ain't I think this ought to be made very clear to the C++ community. Otherwise, people will bend backwards to write the most complicate code in order to be constexpr (it's all about performance, right?), an optimization which then may not even happen at all. Have you seen the latest std::optional proposal? I expect that kind of tricks from the Standard Library, and a literal optional makes sense, but I really hope I never have to write (nor read) code like that.
0 0

chico said on Jan 14, 2013 07:04 PM:

@K-ballo I coudn't say it better. I agree completely. This is the point I wanted to target with emphasis. I see the two sides of the coin (optimization/expressiveness) you talk about and the one you assume is better to assume. Indeed it may work better for one to have that mindset about constexpr (functions et al.) rather than the mindset of it being the tool just perfect to write a translation time factorial.

I know constexpr (functions et al.) is the tool just perfect to write a factorial function that can, also, be used as the size for an array declaration. And that, it's not because it can do that, that it fits equally well for a general translation time calculation trigger, the optimization mindset.

It's not that I particularly think one (mindset) is good and other is bad. The question is what's being assumed and if it's a misconception or not. The question whether that affects the community is a concern to think over...

Short sentences programmers may hear here and there like, "hey we now have constexpr functions that do compilation time evaluation when you pass them compilation time entities, like literals" can induce subtle wrong ideas (or not, if they really do that).
0 0

Bjarne Stroustrup said on Jan 14, 2013 08:58 PM:

It seems that I was too terse and caused confusion. Sorry. The correct answer - as stated by Herb - is that according to the standard a constexpr function may be evaluated at compiler time or run time unless it is used as a constant expression, in which case it must be evaluated at compile-time. To guarantee compile-time evaluation, we must either use it where a constant expression is required (e.g., as an array bound or as a case label) or use it to initialize a constexpr. I would hope that no self-respecting compiler would miss the optimization opportunity to do what I originally said: "A constexpr function is evaluated at compile time if all its arguments are constant expressions."

I have not tried the latest compilers. What do they do?
0 0

chico said on Jan 14, 2013 10:15 PM:

@Bjarne Stroustrup, the behavior seems dependent of optimization level and varies from compiler to compiler. I've tested the following on gcc 4.7 and clang 3.0 on http://gcc.godbolt.org.


#include <cstdlib>

constexpr int f( int i ) { return i + i; }

int main()
{
int i = std::rand();
int j = f(10); // C: compile time or run time

return j + i;
}


It's clear when the compiler computes it at compile time when 20 (=f(10)) appears in assembly (and there's no call f).

clang -O1: run time
clang -O2: compile time
gcc -O0: run time
gcc -O1: compile time
gcc -O2: compile time

To my surprise, they are going the "just literal input is sufficient" route (although there's no guarantee of that).
0 0

chico said on Jan 14, 2013 10:27 PM:

In truth, clang at 3.0 seems buggy at that site. Changing j to constexpr int don't compile. gcc is ok, and putting it at -O0 produces runtime evaluation, but changing j to constexpr int forces the compile time evaluation as required (both return and input constexpr == compile time for sure).
0 0

Cedric said on Jan 15, 2013 01:52 AM:

There are so many cases where the actual code depend on the compiler ability to optimize, I don't see constexpr being neither an exception nor a problem?

This is true for absolutely everything you write in a high level language, as Herb mentionned if you write "a = 21 + 21" it is up to the compiler to decide when is is going to be evaluated. It has always been the case in many other languages too. And we all know that very quickly all compilers will do the job.

I see constexpr mainly as a convenient tool to help the optimizer or to avoid "pre computing" by hand things to achieve ultimate optimization, or complex "static constructor" patterns to be able to call a function to initialize a static.
0 0

chico said on Jan 15, 2013 06:05 AM:

@Cedric constexpr is not a problem in either way, the problem arises when misconceptions start being assumed and the point where it can happen was shown.
0 0

Bjarne Stroustrup said on Jan 15, 2013 08:31 AM:

Cedric's comments make a lot of sense.

We rely on optimizers all the time for performance and constexpr improves our ability to express things sensibly for compile-time computation (rather than relying on hand-precalculation or typeless macro or template hackery). At other times, we depend on compile-time calculation for logical reasons (e.g., to set array bounds or to avoid race conditions in initialization of concurrent systems; then we can use "constexpr" as on an object definition.

For more information about the motivation for constexpr, see the introductory sections of the research paper: http://www.stroustrup.com/sac10-constexpr.pdf
0 0

H Sutter said on Jan 15, 2013 10:27 AM:

Trying to summarize: constexpr isn't about forcing execution to happen at compile time always. It's about enabling programmers to more easily write functions and variables that, in addition to their normal uses, are also guaranteed to be legally usable where the language requires compile-time "constant expressions" (e.g., to initialize enumerator values and array bounds).

The keyword "constexpr" is intended to connote that: "can be used in constant expressions." Granted, "constant expressions" is a precise standardese term, so we're always open to better ways of teaching this.