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 (13)

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.
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

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