P0595
To: EWG
From: Daveed Vandevoorde (daveed@edg.com
)
Date: 2017-02-02
constexpr
OperatorA constexpr
function may be evaluated at compile time or at run time. However, we may want to vary an implementation depending on which context the function is evaluated in. Specifically, when computing a constant-expression, the algorithm has to use portable, standard C++ appearing in the current translation unit, but run-time computations may want to take advantage of platform-specific facilities and/or separately-compiled libraries.
I propose a new operator invoked as constexpr()
. It evaluates to true
when invoked in a context that requires or—for static-lifetime initializers—prefers a constant-expression. It evaluates to false
when invoked as part of an expression that is not a core constant-expression. In any other context, either true
or false
is produced, although the expectation is that true
will be produced for compile-time evaluations and false
otherwise (however, that is difficult to mandate because the standard doesn’t formally distinguish between compile time and run time).
For example:
constexpr double power(double b, int x) {
if (constexpr() && x >= 0) {
/* A constant-evaluation context. */
double r = 1.0, p = b;
unsigned u = (unsigned)x;
while (u != 0) {
if (u & 1) r *= p;
u /= 2;
p *= p;
}
return r;
} else {
/* Let the code generator figure it out. */
return std::pow(b, (double)x);
}
}
constexpr double kilo = power(10.0, 3); // (1)
int n = 3;
double mucho = power(10.0, n); // (2)
double thousand() {
return power(10.0, 3);
}
Call (1) occurs in a constant-expression context, and, therefore, constexpr()
will be true
during the computation of power(10.0, 3)
, which in turn allows the evaluation to complete as a constant-expression.
Call (2) isn’t a constant-expression because n
cannot be converted to an rvalue in a constant-expression context. So it will be evaluated in a context where constexpr()
is false
; this is known at translation time, and the run-time code generated for the function can therefore easily be reduced to the equivalent of just
inline double power'(double b, int x) {
return std::pow(b, (double)x);
}
Call (3) could go either way. The call is technically a constant-expression, but it need not be evaluated as one (it’s not called in a context requiring a constant). The expectation is that if the implementation attempts to evaluate the call at compile time, constexpr()
will produce true
and otherwise it will produce false
(that also falls out of the most likely implementation strategies). In theory, however, a (suspect) implementation could, e.g., evaluate constexpr()
to true
while emitting run-time code.
The implementation of this feature is almost trivial: An operator is added in the front end and that operator is always evaluated as true
within the front end proper (e.g., the constexpr
interpreter) and as false
in the stage that emits code to a back end (or middle end).