For this function, and many others like it, it is sometimes desirable to
allow compile time evaluation (for use in array bounds or enumerators). However,
under the current language rules, we cannot mark this function as
constexpr without completely rewriting it:

Such rewrites can be performed mechanically by the programmer to remove the
uses of local variable declarations and if statements from functions,
and thus allow them to be marked constexpr. However, this makes the
resulting code harder to read and understand, and forces an awkward coding style
on the programmer. There is no reason to require the programmer to go to this
effort.

Note that a helper function is required in the rewrite, to avoid computing
sqrt multiple times. This is necessary even for implementations which
aggressively cache constexpr evaluations, in order to give good
performance if the code is used in a context which is not evaluated at
translation time.

Solution

Allow arbitrary code in constexpr function definitions, with three
exceptions:

Since modification of the values of variables is not permitted in core
constant expressions, iteration statements cannot beneficially be used in
constexpr functions, so they are disallowed.

switch and goto statements are disallowed, to avoid the
need to model complex control flow and possible infinite loops.

As with C++11, it must be possible for at least one control flow path
through the function to produce a core constant expression.

Specific changes

This paper proposes applying the following changes. These choices are
largely independent. All references to constexpr functions here also apply to
constexpr constructors, unless otherwise indicated.

Local variable declarations

Declarations of variables with automatic storage duration will be permitted
within constexpr function definitions, so long as they have literal
type and, if a constructor is called to perform their initialization, that
constructor is a constexpr constructor. These can currently be
simulated using a helper function that binds the variables to parameters, and
the behavior of local variables would match the behavior of such parameters. In
particular, there is no requirement that the variables be const, but
any attempt to modify them would cause constant evaluation to fail (so the call
will be deferred to runtime).

Static variable declarations

Declarations of variables with static and thread storage duration will be
permitted within constexpr functions, with some restrictions. Firstly,
such a variable cannot have dynamic initialization. If it did, the initial value
of the variable could depend on the order in which the implementation chose to
evaluate constexpr function calls:

Secondly, such a variable cannot have a non-trivial destructor. This allows
an implementation to evaluate a constexpr function call at will,
without any concern about whether such evaluation causes a side-effect at
program termination.

In all other respects, such static or thread_local
variables can be used within constexpr functions in the same ways that
they could be used if they were declared outside the function. In particular,
they do not need to be constexpr nor have a literal type if their value
is not used:

Type definitions

Type definitions, including enum and class definitions,
will be permitted without restriction within constexpr functions. There
appear to be no implementation or practical reasons which warrant their
restriction, and they can be useful for tracking local state of a
constexpr function, or as a comparator or similar object to be used as
an argument to a suitable constexpr algorithm.

This example would also benefit from lambdas being permitted in
constexpr functions, but that is not proposed in this paper.

if statements and multiple return statements

if-statements will be permitted, as a more natural syntactic
alternative to the existing support for ? : expressions. The rule that
a constexpr function must have exactly one return statement
will be relaxed to requiring at least one return statement, so
that each branch of an if-statement can return a value. For
constexpr constructors, any number of return statements will
be permitted.

If a control flow path is taken through a constexpr function which
does not reach a return statement or a throw expression, the
behavior of the program is undefined, so constant evaluation fails.

Compound statements

compound-statements will be permitted, in order to allow
if-statements to control complex computations involving variable
declarations.

Expression statements

expression-statements will be permitted. Since no side-effects can
occur inside a constant expression, the only effect of an
expression-statement during function invocation substitution can be to
render the expression non-constant. However, that effect can be desirable in
some cases. For instance:

An arbitrary expression-statement is permitted, in order to allow
calls to functions performing checks and to allow assert-like constructs.
void also becomes a literal type, so that constexpr functions
which exist only to perform such checks may return void.

Semantics

In each case, the construct behaves identically within function invocation
substitution as it would if the function were evaluated at runtime. The values
of local variables are substituted into expressions prior to their evaluation,
in the same manner that function argument values are substituted for references
to the function parameters. If any evaluated expression is not a core constant
expression after substitution, the function call is also not a core constant
expression.

Implementation

An implementation of an earlier revision of this proposal is available
here.