Are you allowed to use static
local variables in constexpr
functions? For example:
#include <string_view>
#include <utility>
enum class axis {
x, y, z
};
constexpr std::string_view axis_name(axis a) {
// use static constexpr to avoid putting the table onto the stack
static constexpr std::string_view names[] {
"x", "y", "z"
};
return names[std::to_underlying(a)];
}
constexpr auto x_name = axis_name(axis::x);
GCC 12 fails to compile this with the error:
<source>:9:39: error: 'names' defined 'static' in 'constexpr' context
9 | static constexpr std::string_view names[] {
| ^~~~~
Other compilers allow it. What are the rules, and when is it allowed?
static
in general, orstatic const
, orstatic constexpr
?This code is okay since C++23, because the restrictions on static constexpr
have been lifted.
Until C++23, it was not only illegal to initialize a static
local in a constexpr
function, it was also illegal to declare one, even if control didn't flow through it.
For example:
constexpr void foo() {
if (false) {
// allowed since C++23, otherwise ill-formed
// ok because control never flows through x
static int x;
}
// allowed since C++23, otherwise ill-formed
// ok because static constexpr
static constexpr int y = 0;
}
Disallowing static constexpr
variables has always been an arbitrary restriction, lifted by P2647 - Permitting static constexpr
variables in constexpr
functions.
To use this feature, you must use a recent compiler. As of the time of writing, this is the compiler support:
C++23 Feature | Paper | GCC | Clang | MSVC |
---|---|---|---|---|
Permitting static constexpr variables in constexpr functions |
P2647R1 | 13 | 16 | / |
See also: C++23 compiler support page on cppreference
static
In General?It is unclear how static
objects should behave at compile time, and how this could be implemented consistently across all compilers. For example, consider:
constexpr std::size_t get_size() {
static std::size_t s = 0;
// different result each time get_size() is called
return ++s;
}
// what are A and B??? B = 1, and B = 2 are both reasonable
template <std::size_t A = get_size(), B = get_size()>
constexpr std::size_t foo() { return A + B; }
It's easy to see static
introduces a huge amount of problems and questions when used at compile time, so it will likely never be unrestricted. It breaks the assumption that constexpr
functions are pure (have no side effects), and also makes it unsafe to memoize them (cache their results to call them less often).
static const
static const
is also problematic, because it could be initialized to a function argument:
constexpr int remember_first_call(int x) {
static const int y = x;
return y;
}
This function will always return the argument that it has first been called with, which introduces an idea of "state" and "time" at compile time that shouldn't exist.
However, there is one exception to the rule for static const
:
constexpr int foo() {
// allowed since C++23
// ok because const integers are implicitly constexpr when possible
static const int x = 3;
return x;
}