I got this example from §5.19/2 in N4140:
constexpr int incr(int &n) {
return ++n;
}
As far as I can tell, this is not a constexp
As far as I can tell, this is not a
constexpr
function.
Why do you say that? The example from §5.19/2 reads:
constexpr int g(int k) {
constexpr int x = incr(k); // error: incr(k) is not a core constant
// expression because lifetime of k
// began outside the expression incr(k)
return x;
}
incr(k)
not being a core constant expression does not mean incr
cannot not be a constexpr function.
Under C++14's constexpr rules, it is possible to use incr
in a constexpr context, for example:
constexpr int incr(int& n) {
return ++n;
}
constexpr int foo() {
int n = 0;
incr(n);
return n;
}
Unless it's downright impossible for the body of the function to be constexpr
(for example, calling a non-constexpr function unconditionally), the compiler has no reason to produce an error at the point of definition.
A constexpr function may even contain paths/branches in the body which would not be constexpr. As long as they are never taken in a constexpr context, you will not get an error. For example:
constexpr int maybe_constexpr(bool choice, const int& a, const int& b) {
return choice ? a : b;
}
constexpr int a = 0;
int b = 1;
static_assert(maybe_constexpr(true, a, b) == 0, "!");
live example
In C++14 the rules for constexpr function were relaxed and the paper N3597: Relaxing constraints on constexpr functions. The paper goes into the rationale and the effects and it includes the following (emphasis mine):
As in C++11, the constexpr keyword is used to mark functions which the implementation is required to evaluate during translation, if they are used from a context where a constant expression is required. Any valid C++ code is permitted in constexpr functions, including the creation and modification of local variables, and almost all statements, with the restriction that it must be possible for a constexpr function to be used from within a constant expression. A constant expression may still have side-effects which are local to the evaluation and its result.
and:
A handful of syntactic restrictions on constexpr functions are retained:
- asm-declarations are not permitted.
- try-blocks and function-try-blocks are not permitted.
- Declarations of variables with static and thread storage duration have some restrictions (see below).
and we can find this covered in N4140 section 7.1.5
[dcl.constexpr] which says:
The definition of a constexpr function shall satisfy the following constraints:
it shall not be virtual (10.3);
its return type shall be a literal type;
each of its parameter types shall be a literal type;
its function-body shall be = delete, = default, or a compound-statement that does not contain
an asm-definition,
a goto statement,
a try-block, or
a definition of a variable of non-literal type or of static or thread storage duration or for which no initialization is performed.
The last example shows how incr
can be used in a constexpr:
constexpr int h(int k) {
int x = incr(k); // OK: incr(k) is not required to be a core
// constant expression
return x;
}
constexpr int y = h(1); // OK: initializes y with the value 2
// h(1) is a core constant expression because
// the lifetime of k begins inside h(1)
and the rule that covers the lifetime of k begins inside h(1)
is:
- modification of an object (5.17, 5.2.6, 5.3.2) unless it is applied to a non-volatile lvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of e;
The wording in 7.1.5
[dcl.constexpr] shows us why incr
is a valid constexpr:
For a non-template, non-defaulted constexpr function or a non-template, non-defaulted, non-inheriting constexpr constructor, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression (5.19), the program is ill-formed; no diagnostic required.
As the modified example given by T.C.:
constexpr int& as_lvalue(int&& i){ return i; }
constexpr int x = incr(as_lvalue(1)) ;
shows, we can indeed use incr
as a subexpression of a core constant expression and therefore it is not ill-formed.