Despite this paper missing both our respective NB comment deadlines and the mailing deadline, we still believe it provides a significant enough improvement to the status quo that it should be considered for C++20.
C++20 will have several new features to aid programmers in writing code during constant evaluation. Two of these are
std::is_constant_evaluated() [P0595R2] and
consteval [P1073R3], both adopted in San Diego 2018.
consteval is for functions that can only be invoked during constant evaluation.
is_constant_evaluated() is a magic library function to check if the current evaluation is constant evaluation to provide, for instance, a valid implementation of an algorithm for constant evaluation time and a better implementation for runtime.
However, despite being adopted at the same meeting, these features interact poorly with each other and have other issues that make them ripe for confusion.
There are two problems this paper wishes to address.
The first problem is the interplay between this magic library function and the new
consteval. Consider the example:
h here is basically a lifted, constant-evaluation-only version of the function
g. At constant evaluation time, they do the same thing, except that during runtime, you cannot call
g has this extra path. Maybe this code started with just
h and someone decided a runtime version would also be useful and turned it into
h is well-formed while
g is ill-formed. You cannot make that call to
f (that is ominously marked with an arrow) in that location. Even though that call will only happen during constant evaluation, that’s still not enough.
With specific terms, the call to
f() inside of
g() is an immediate invocation and needs to be a constant expression and it is not. Whereas the call to
f() inside of
h() is not considered an immediate invocation because it is in an immediate function context (i.e. it’s invoked from another immediate function), so it has a weaker set of restrictions that it needs to follow.
In other words, this kind of construction of conditionally invoking a
consteval function from a
constexpr function just Does Not Work (modulo the really trivial cases - one could call
f(42) for instance, just never
We find this lack of composability of features to be problematic and think it can be improved.
The second problem is specific to
is_constant_evaluated. Once you learn what this magic function is for, the obvious usage of it is:
This example, inspired by [P1045R0], has a bug: it uses
if constexpr to check the conditional
is_constant_evaluated() rather than a simple
if. You have to really deeply understand a lot about how constant evaluation works in C++ to understand that this is in fact not only not “obviously correct” but is in fact “obviously incorrect,” for some definition of obvious. This is such a likely source of error that Barry submitted bugs to both gcc and clang to encourage the compilers to warn on such improper usage. gcc 10.1 will provide a warning for the simple case:
But then people have to understand why this is a warning, and what this even means. Nevertheless, a compiler warning is substantially better than silently wrong code, but it is problematic to have an API in which many users are drawn to a usage that is tautologically incorrect.
We propose a new form of
if statement which is spelled:
The braces (in both the
if and the optional
else) are mandatory and there is no condition. If evaluation of this statement occurs during constant evaluation, the first substatement is executed. Otherwise, the second substatement (if there is one) is executed.
This behaves exactly as today’s:
except with three differences:
if constevalto allow invoking immediate functions.
To explain the last point a bit more, the current language rules allow you to invoke a
consteval function from inside of another
consteval function ([expr.const]/12) - we can do this by construction:
An expression or conversion is in an immediate function context if it is potentially evaluated and its innermost non-block scope is a function parameter scope of an immediate function. An expression or conversion is an immediate invocation if it is an explicit or implicit invocation of an immediate function and is not in an immediate function context. An immediate invocation shall be a constant expression.
By extending the term immediate function context to also include an
if consteval block, we can allow the second example to work:
Additionally, such a feature would allow for an easy implementation of the original
Which in itself suggests that this is the more fundamental feature. As such,
std::is_constant_evaluated() may itself no longer be necessary. However, given the very late date of this proposal, we would more than happily keep it if it allows us this new language feature.
The initial revision of the
std::is_constant_evaluated() proposal [P0595R0] was actually targeted as a language feature rather than a library feature. The original spelling was
if (constexpr()). The paper was presented in Kona 2017 and was received very favorably in the form it was presented (17-4). The poll to consider a magic library alternative was only marginally more preferred (17-3). We believe that in the two years since these polls were taken, having a dedicated language feature with an impossible-to-misuse API, that can coexist with the rest of the constant ecosystem, is the right direction.
Extend the definition of immediate function context in 7.7 [expr.const] (and use bullet points):
An expression or conversion is in an immediate function context if it is potentially evaluated and either:
Change 8.5 [stmt.select] to add the new grammar:
Add a new clause to 8.5.1 [stmt.if]
ifstatement is of the form
if constevaland evaluation occurs in a context that is manifestly constant-evaluated ([expr.const]), the first substatement is executed. Otherwise, if the
elsepart of the selection statement is present, then the second substatement is executed.
Change 20.15.10 [meta.const.eval] to use this new functionality:
trueif and only if evaluation of the call occurs within the evaluation of an expression or conversion that is manifestly constant-evaluated ([expr.const]).
1 Effects: Equivalent to:
Thank you to David Stone and Tim Song for working through these examples.
[P0595R0] Daveed Vandevoorde. 2017. The “constexpr” Operator.
[P0595R2] Richard Smith, Andrew Sutton, Daveed Vandevoorde. 2018.
[P1045R0] David Stone. 2018. constexpr Function Parameters.
[P1073R3] Richard Smith, Andrew Sutton, Daveed Vandevoorde. 2018. Immediate functions.