Reputation: 3513
For example the code below doesn't compile unless incr()
is declared constexpr
:
int incr(int& n) {
return ++n;
}
constexpr int foo() {
int n = 0;
incr(n);
return n;
}
Looking at §7.1.5/3 in C++14 we have:
The definition of a constexpr function shall satisfy the following constraints:
(3.1) — it shall not be virtual (10.3);
(3.2) — its return type shall be a literal type;
(3.3) — each of its parameter types shall be a literal type;
(3.4) — its function-body shall be = delete, = default, or a compound-statement that does not contain(3.4.1) — an asm-definition,
(3.4.2) — a goto statement,
(3.4.3) — a try-block, or
(3.4.4) — a definition of a variable of non-literal type or of static or thread storage duration or for which no initialization is performed.
Upvotes: 8
Views: 713
Reputation: 88215
What you're looking for is § 5.19:
A conditional-expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (1.9), would evaluate one of the following expressions:
This applies to the evaluation of an expression that is a constexpr
function call. That is, calling a constexpr
function will be a 'core constant expression' if evaluating the function, i.e. executing the body of the function according to the rules of the C++ abstract machine, doesn't do any of the things forbidden in the list given in § 5.19.
One of items in the list is:
- an invocation of a function other than [...] a constexpr function
So to apply this to your example: evaluating the expression foo()
evaluates a call to a function incr()
which is not a constexpr function, which means that the expression foo()
is not a core constant expression.
Further, since the above is true for all possible invocations of your function foo
, the rule in § 7.1.5/5 kicks in and means that your example program is ill-formed, no diagnostic required, even if you never actually call foo()
.
As Ben Voigt points out a constexpr function can contain calls to non-consexpr functions, so long as the particular evaluation of the function does not actually evaluate any such function call (or it appears in a context that does not require a constant expression).
The restrictions in 5.19 only pertain to what expressions actually end up being evaluated as part of the evaluation of an expression.
For example:
#include <iostream>
int incr(int &n) { return ++n; }
enum E {be_constexpr, not_constexpr};
constexpr int foo(E e = be_constexpr) {
int n = 0;
if (e == not_constexpr) { incr(n); }
return n;
}
int main() {
constexpr int a = foo(); // foo() is a constant expression
int b = foo(not_constexpr); // may or may not evaluate `foo(non_constexpr)` at runtime. In practice modern C++ compilers will do compile-time evaluation here even though they aren't required to.
// constexpr int c = foo(not_constexpr); // Compile error because foo(not_constexpr) is not formally a constant expression, even though modern compilers can evaluate it at compile-time.
std::cout << a << ' ' << b << '\n';
}
Upvotes: 8
Reputation: 303537
Two paragraphs later, in [dcl.constexpr]/5:
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.20), or, for a constructor, a constant initializer for some object (3.6.2), the program is ill-formed; no diagnostic required.
No argument exists such that foo()
could be a core constant expression because of incr()
, therefore the program is ill-formed (NDR).
Upvotes: 10
Reputation: 283793
It doesn't.
The following is allowed, even though it does exactly what you surmise is forbidden:
int incr(int& n) {
return ++n;
}
constexpr int foo(bool x) {
int n = 0;
if (x) incr(n);
return n;
}
The code in your question is disallowed by the rule with Barry quoted in his answer. But in my variation, there does exist a set of parameters (specifically, false
) with which invocation of foo
results in a compile-time constant expression.
Note that a diagnostic isn't required -- a conforming compiler could allow your version to compile as well.
Upvotes: 4