The rule about constexpr
variables is [dcl.constexpr]/6,
[...] In any constexpr
variable declaration, the full-expression of the initialization shall be a constant expression ([expr.const]). A constexpr
variable that is an object, as well as any temporary to which a constexpr
reference is bound, shall have constant destruction.
Every constant expression must be a core constant expression, with some additional restrictions that are not relevant here, so I won't get into them ([expr.const]/13). If the initialization of a constexpr
variable is not a core constant expression, the program is ill-formed.
The particular rule violated by the second example is [expr.const]/5.9.
An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following:
- [...]
- an lvalue-to-rvalue conversion unless it is applied to
- a non-volatile glvalue that refers to an object that is usable in constant expressions, or
- a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of E;
In the declaration
constexpr int x = incr(k);
the full-expression "initialize an int
variable from incr(k)
" fails to be a core constant expression because, when it is evaluated, it needs to perform an lvalue-to-rvalue conversion on k
in order to get the value to initialize k1
with. The variable k
is not usable in constant expressions, nor did its lifetime begin within the full-expression "initialize an int
variable from incr(k)
".
In the first example, which your compiler accepts, incr(k)
is also being evaluated at compile time, but it is not required to be a core constant expression, so there is no problem. It's very confusing at first, but we need to remember that something that is not a core constant expression can be evaluated as part of a "bigger" evaluation that is a core constant expression. In this case, it is the enclosing constexpr
variable initialization (that of y
) that is required to be a core constant expression—and it is, because it creates the function parameter k
(initializing it with the value 4) and then reads from it. To put it another way, if E is "initialize y
with foo(4)
", then the lifetime of k
begins within E, so the read of k
can occur without preventing E from being a core constant expression.