6

As far as I understand it, constexpr can be seen as a hint to the compiler to check whether given expressions can be evaluated at compile-time and do so if possible.

I know that it also imposes some restriction on the function or initialization declared as constexpr but the final goal is compile-time evaluation, isn't it?

So my question is, why can't we leave that at the compiler? It is obviously capable of checking the pre-conditions, so why doesn't it do for each expression and evaluate at compile-time where possible?

I have two ideas on why this might be the case but I am not yet convinced that they hit the point:

a) It might take too long during compile-time.

b) Since my code can use constexpr functions in locations where normale functions would not be allowed the specifier is also kind of part of the declaration. If the compiler did everything by itself, one could use a function in a C-array definition with one version of the function but with the next version there might be a compiler-error, because the pre-conditions for compile-time evaluation are no more satisfied.

cigien
  • 57,834
  • 11
  • 73
  • 112
Kit Fisto
  • 4,385
  • 5
  • 26
  • 43

4 Answers4

5

constexpr is not a "hint" to the compiler about anything; constexpr is a requirement. It doesn't require that an expression actually be executed at compile time; it requires that it could.

What constexpr does (for functions) is restrict what you're allowed to put into function definition, so that the compiler can easily execute that code at compile time where possible. It's a contract between you the programmer and the compiler. If your function violates the contract, the compiler will error immediately.

Once the contract is established, you are now able to use these constexpr functions in places where the language requires a compile time constant expression. The compiler can then check the elements of a constant expression to see that all function calls in the expression call constexpr functions; if they don't, again a compiler error results.

Your attempt to make this implicit would result in two problems. First, without an explicit contract as defined by the language, how would I know what I can and cannot do in a constexpr function? How do I know what will make a function not constexpr?

And second, without the contract being in the compiler, via a declaration of my intent to make the function constexpr, how would the compiler be able to verify that my function conforms to that contract? It couldn't; it would have to wait until I use it in a constant expression before I find that it isn't actually a proper constexpr function.

Contracts are best stated explicitly and up-front.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • 1
    "The compiler will error immediately" - that's not quite true. There are cases that "ill-formed but no diagnostics required" stated by the related rules in the standard. – FrankHB Mar 27 '16 at 14:20
3

constexpr can be seen as a hint to the compiler to check whether given expressions can be evaluated at compile-time and do so if possible

No, see below

the final goal is compile-time evaluation

No, see below.

so why doesn't it do for each expression and evaluate at compile-time where possible?

Optimizers do things like that, as allowed under the as-if rule.

constexpr is not used to make things faster, it is used to allow usage of the result in context where a runtime-variable expression is illegal.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
2

This is only my evaluation, but I believe your (b) reason is correct (that it forms part of the interface that the compiler can enforce). The interface requirement serves both for the writer of the code and the client of the code.

The writer may intend something to be usable in a compile-time context, but not actually use it in this way. If the writer violates the rules for constexpr, they might not find out until after publication when clients who try to use it constexpr fail. Or, more realistically, the library might use the code in a constexpr sense in version 1, refactor this usage out in version 2, and break constexpr compatibility in version 3 without realizing it. By checking constexpr-compliance, the breakage in version 3 will be caught before deployment.

The interface for the client is more obvious --- an inline function won't silently become constexpr-required because it happened to work and someone used that way.

I don't believe your (a) reason (that it could take too long for the compiler) is applicable because (1) the compiler has to check much of the constexpr constraints anyway when the code is marked, (2) without the annotation, the compiler would only have to do the checking when used in a constexpr way (so most functions wouldn't have to be checked), and (3) IIUC the D programming language actually does allow functions to be compile-time evaluated if they meet requirements without any declaration assistance, so apparently it can be done.

Adam H. Peterson
  • 4,511
  • 20
  • 28
  • 4
    C++ allows functions to be compile-time evaluated without any declaration assistance. The `constexpr` modifier is only needed if you want to use the result in a constant context. But non-`constexpr` functions are still eligible for precomputation and other optimizations. – Ben Voigt Jul 15 '13 at 18:20
  • 2
    @BenVoigt, yes, that's true, but as far as I know, a compiler is free to evaluate such functions either at compile time or at runtime unless `constexpr` is used, which means compile-time evaluation isn't an interface-oriented concept unless used in a constant context. That is, generally a function that was previously evaluated at compile time may subsequently be evaluated at runtime (after a refactoring or by a different compiler) without breaking code that depended on it. – Adam H. Peterson Jul 15 '13 at 18:35
  • @AdamH.Peterson: "*which means compile-time evaluation isn't an interface-oriented concept unless used in a constant context.*" Which is *exactly* why `constexpr` exists: so that you *can* use user-defined functions in a "constant context". Which you can't without it. – Nicol Bolas Jul 15 '13 at 20:55
0

I think I remember watching an early talk by Bjarne Stroustrup where he mentioned that programmers wanted fine grained control on this "dangerous" feature, from which I understand that they don't want things "accidentally" executed at compile time without them knowing. (Even if that sound like a good thing.)

There can be many reasons for that, but the only valid one is ultimatelly compilation speed I think ( (a) in your list ). It would be too much burden on the compiler to determine for every function if it could be computed at compile time. This argument is weaker as compilation times in general go down.

Like many other features of C++ what end up happening is that we end up with the "wrong defaults". So you have to tell when you want constexpr instead of when you don't want constexpr (runtimeexpr); you have to tell when you want const intead of where you want mutable, etc.

Admitedly, you can imagine functions that take an absurd amount of time to run at compile time and that cannot be amortized (with other kind of machine resources) at runtime. (I am not aware that "time-out" can be a criterion in a compiler for constexpr, but it could be so.) Or it could be that one is compiling in a system that is always expected to finish compilation in a finite time but an unbounded runtime is admissible (or debuggable).

I know that this question is old, but time has illuminated that it actually makes sense to have constexpr as default:

In C++17, for example, you can declare a lambda constexpr but more importantly they are constexpr by default if they can be so.

https://learn.microsoft.com/en-us/cpp/cpp/lambda-expressions-constexpr

Note that lambda has all the "right" (opposite) defaults, members (captures) are const by default, arguments are templates by default auto, and now these functions are constexpr by default.

alfC
  • 14,261
  • 4
  • 67
  • 118