72

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?

Byzantian
  • 3,208
  • 4
  • 27
  • 31
  • AFAIK, when all arguments are constant expressions. – chris Jan 09 '13 at 23:19
  • @chris And what if I write `POW((unsigned __int64)2, 63)`. Would that still count as a constant expression? – Byzantian Jan 09 '13 at 23:21
  • 10
    @chris: Actually, it's more complex than that I think. I _think_ `constexpr` is only required to be evaluated when its result is used as a template parameter, array bound, or other integral constant. Any other time is an _optimization_. In fact, even when given constant expression arguments, it might be _required_ to execute at runtime. `constexpr int func(int p) { return !p ? 1 : throw std::exception("HI");}` _must_ be evaluated at runtime when given a non-zero input. – Mooing Duck Jan 09 '13 at 23:21
  • Initializers that are constant expressions form part of the static initialization phase, e.g. `constexpr int a = POW(5, 4);`. That's essentially computed at compile time. But you can of course still use `POW` in other places. – Kerrek SB Jan 09 '13 at 23:23
  • @MooingDuck: Unless the result of the function is being used in your aforementioned constant expression "requirerers", then it will give a compile-time error because of the exception. – GManNickG Jan 09 '13 at 23:34
  • @GManNickG: I ran out of comment space, but yes :D – Mooing Duck Jan 10 '13 at 00:01
  • related: http://stackoverflow.com/questions/14294271/forcing-a-constant-expression-to-be-evaluated-during-compile-time – WorldSEnder Jun 30 '15 at 16:01

2 Answers2

108

constexpr functions will be evaluated at compile time when all its arguments are constant expressions and the result is used in a constant expression as well. A constant expression could be a literal (like 42), a non-type template argument (like N in template<class T, size_t N> class array;), an enum element declaration (like Blue in enum Color { Red, Blue, Green };, another variable declared constexpr, and so on.

They might be evaluated when all its arguments are constant expressions and the result is not used in a constant expression, but that is up to the implementation.

Herb Sutter
  • 1,888
  • 1
  • 14
  • 14
K-ballo
  • 80,396
  • 20
  • 159
  • 169
  • I'm trying to conceive of a general way to assert the compile-time evaluation, something like `STATIC_ASSERT_CONSTEXPR( pow(2,3) )`, but I'm not having a lot of luck... – mcmcc Jan 11 '13 at 19:28
  • 2
    @mcmcc try using the result as an non-type template parameter. this should cover integral constexpr. for float you need something else. – TemplateRex Jan 11 '13 at 19:39
  • "A constexpr function is evaluated at compile time if all its arguments are constant expressions" --Bjarne Stroustrup said on Jan 11, 2013 07:27 PM: http://isocpp.org/blog/2013/01/when-does-a-constexpr-function-get-evaluated-at-compile-time-stackoverflow ? – oblitum Jan 14 '13 at 00:56
  • 4
    Where in the Standard is there a guarantee that `constexpr` functions are evaluated at compile time if all arguments are constant expressions? I believe the only thing the Standard says is that `constexpr` functions can be used in contexts that must be evaluated at compile time, e.g. template arguments. Anything else is up to the compiler to decide. At least that is what I've believed so far. – jogojapan Jan 16 '13 at 04:36
  • 1
    @jogojapan: ...that's exactly what my answer says. You can check the ISO C++ link for more debate on the subject. – K-ballo Jan 16 '13 at 05:15
  • @K-ballo Say I have a `constexpr int f();` and then declare a constant expression `constexpr int a = f();`. Your answer suggests that `a` will be evaluated at compile time, because `f` is `constexpr`, all its arguments are too, and the result is used in a `constexpr`. But I believe this is _necessarily_ true only if `a` is used in context that requires compile-time evaluation, e.g. a template argument. Also, I have seen the link, but since this is an SO answer, the relevant facts, esp. Standard quotes, should be put here. – jogojapan Jan 16 '13 at 05:34
  • @jogojapan: A `constexpr` variable is a _constant expression_, which requires compile-time evaluation. The standard quotes are non-normative notes; the requirements for `constexpr` functions to be evaluated at compile-time are implicitly made all over the standard whenever it talks about a _constant expression_. – K-ballo Jan 16 '13 at 05:58
  • A constant expression does not require compile-time evaluation. 5.19/4: _Note: Although in some contexts, constant expressions must be evaluated during program translation, others may be evaluated during program execution_ – jogojapan Jan 16 '13 at 06:18
  • @jogojapan: That's a note, so its not normative... Back to your example, `a` could then be used as a template-argument or other context that requires compile-time evaluation, which _I believe_ implicitly requires its evaluation to be always done at compile-time. But I see your point, you should ask at the _ISO C++_ post to get the attention from those involved in the making of this feature... – K-ballo Jan 16 '13 at 06:29
  • The note isn't normative, but if your statement above was true, the note would be downright _wrong_. Actually, Herb says in the ISO C++ post already that Bjarne _should_ have said: "A constexpr function _can be_ evaluated at compile time if all its arguments are constant expressions". This is a very important point. The Standard leaves it open whether the constant expression is evaluated at compile time. And that's why the question here asks: What do compilers actually do? What is the criterion compilers actually use? I'd like to know myself, too... – jogojapan Jan 16 '13 at 06:34
  • 1
    @jogojapan: According to both Bjarne and Herb back at the _ISO C++_ post, _"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."_. That would mean that `a` in your example _must_ be evaluated at compile-time, and doesn't make the note downright _wrong_. I'm not sure this is actually guaranteed by the standard at this point... – K-ballo Jan 16 '13 at 06:37
  • @K-ballo Frankly, what Bjarne says in this statement is not extremely helpful. He implicitly introduces the new concept of _is used as constant expression_ (as opposed to: _is a constant expression_). Every expression marked `constexpr` is a constant expression, but it is only guaranteed to be evaluated at compile-time if it is _used as_ a constant expression (e.g. used as template argument). But what exactly does that mean? – jogojapan Jan 16 '13 at 06:52
  • @jogojapan: I'm inclined to think it means that you got it right, and you are only guaranteed compile-time evaluation if the constant expression is directly or indirectly used in a context that requires compile-time evaluation... Sorry I can't be of any more help. – K-ballo Jan 16 '13 at 07:00
  • 6
    @mcmcc: For your "something else", why not write a `constexpr` function that accepts a floating-point value (or in fact any type you like) and returns an integer, then use that inside a template non-type parameter. `constexpr size_t check_nonintegral_constexpr(float v) { return sizeof v; } std::array assertion;` – Ben Voigt Apr 01 '13 at 21:59
  • 1
    "And the result is used in a constant expression as well". I was struggling with that. Thanks. – karliwson Dec 07 '16 at 03:49
23

The function has to be evaluated at compile-time when a constant expression is needed.

The simplest method to guarantee this is to use a constexpr value, or std::integral_constant:

constexpr auto result = POW(i, 2); // this should not compile since i is not a constant expression
std::cout << result << std::endl;

or:

std::cout << std::integral_constant<int, POW(i, 2)>::value << std::endl;

or

#define POW_C(base, power) (std::integral_constant<decltype(POW((base), (power)), POW((base), (power))>::value)

std::cout << POW_C(63, 2) << std::endl;

or

template<int base, int power>
struct POW_C {
  static constexpr int value = POW(base, power);
};

std::cout << POW_C<2, 63>::value << std::endl;
Pubby
  • 51,882
  • 13
  • 139
  • 180
  • Does that mean that `std::cout << POW(2, 63) << std::endl` could end up not being evaluated during compile-time, since cout doesn't require a constant expression value? – Byzantian Jan 09 '13 at 23:30
  • @cyberpunk_ Yes. In fact, you should assume it will be called at run-time as that's what current compilers seem to do. – Pubby Jan 09 '13 at 23:32
  • Is there a way to force the compiler to evaluate it during compile-time? Preferably in one line. An anonymous constexpr perhaps? – Byzantian Jan 09 '13 at 23:36
  • but then I would sacrifice type independence – Byzantian Jan 09 '13 at 23:42
  • @cyberpunk_ you can write a macro I suppose. Although I would just prefer the two line `constexpr auto` method. – Pubby Jan 09 '13 at 23:45
  • And there really is no one liner way of doing this? How about nesting your solution inside of another constexpr function? I would like to avoid macros whenever possible. – Byzantian Jan 09 '13 at 23:47
  • 1
    @cyberpunk_ The arguments to constexpr functions are not constant expressions and so that wouldn't help. – Pubby Jan 09 '13 at 23:48
  • How about this? An anonymous lambda `std::cout << *[](){static constexpr auto expr = POW(1.113, 11); return &expr;}() << std::endl;` – Byzantian Jan 10 '13 at 00:11
  • @cyberpunk_ Don't return a pointer and instead return a value. That should work. – Pubby Jan 10 '13 at 00:15
  • @cyberpunk_: It stops being a constant expression itself that way, though. – GManNickG Jan 10 '13 at 00:17
  • @Pubby Why not? Doesn't `static` basically mean "place it on the data segment"? What's wrong with a pointer since it can't disappear? @GManNickG But the function will be evaluated at compile-time in any case, correct? – Byzantian Jan 10 '13 at 00:39
  • @cyberpunk_: Right, just the result of the lambda is not a constant expression so there's a "wall" there of sorts. And returning a pointer there just doesn't make sense. You get nothing for it except now you have to dereference it all the time; just return the value directly. – GManNickG Jan 10 '13 at 00:51
  • @GManNickG But I am dereferencing it right before the lambda. `*[]`. I did this to ensure that the return statement doesn't copy the object. – Byzantian Jan 10 '13 at 01:02
  • 1
    @cyberpunk_: You should use references for that. (Not that copying a compile-time value is going to cost anything...) – GManNickG Jan 10 '13 at 01:05
  • Is there a generic way to wrap any constexpr expressions to force compile time. Eg. compile_time(POW(i,j))? I might have wanted that to be compile time but probably forgot to declare i,j as constexpr. Can we catch those errors in compile time not having to write wrappers for every function? – balki Jan 12 '13 at 10:50
  • 5
    @balki Here is what I came up with, but I am not sure if it's the best possible solution. I am also not sure if returning an rvalue reference actually works this way. `#define FORCE_CT_EVAL(func) [](){constexpr auto ___expr = func; return std::move(___expr);}()` – Byzantian Jan 12 '13 at 13:46
  • @cyberpunk_ what prevents the compiler from calling `operator()` of the lambda object? This is exactly what I see with `gdb`. Not only that, `operator()` might not be constexpr itself and you might be forcing run-time evaluation. – user1095108 Sep 21 '13 at 17:05
  • Sadly, all of the template bases syntax tricks you show, do not work with constexpr double functions. So I have to define a named constexr variable in this case (your first solution). – not-a-user Dec 10 '15 at 09:17