Does static constexpr variable inside a function make sense?
The short answer is that not only is static
useful, it is pretty well always going to be desired.
First, note that static
and constexpr
are completely independent of each other. static
defines the object's lifetime during execution; constexpr
specifies that the object should be available during compilation. Compilation and execution are disjoint and discontiguous, both in time and space. So once the program is compiled, constexpr
is no longer relevant.
Every variable declared constexpr
is implicitly const
but const
and static
are almost orthogonal (except for the interaction with static const
integers.)
The C++
object model (§1.9) requires that all objects other than bit-fields occupy at least one byte of memory and have addresses; furthermore all such objects observable in a program at a given moment must have distinct addresses (paragraph 6). This does not quite require the compiler to create a new array on the stack for every invocation of a function with a local non-static const array, because the compiler could take refuge in the as-if
principle provided it can prove that no other such object can be observed.
That's not going to be easy to prove, unfortunately, unless the function is trivial (for example, it does not call any other function whose body is not visible within the translation unit) because arrays, more or less by definition, are addresses. So in most cases, the non-static const(expr)
array will have to be recreated on the stack at every invocation, which defeats the point of being able to compute it at compile time.
On the other hand, a local static const
object is shared by all observers, and furthermore may be initialized even if the function it is defined in is never called. So none of the above applies, and a compiler is free not only to generate only a single instance of it; it is free to generate a single instance of it in read-only storage.
So you should definitely use static constexpr
in your example.
However, there is one case where you wouldn't want to use static constexpr
. Unless a constexpr
declared object is either ODR-used or declared static
, the compiler is free to not include it at all. That's pretty useful, because it allows the use of compile-time temporary constexpr
arrays without polluting the compiled program with unnecessary bytes. In that case, you would clearly not want to use static
, since static
is likely to force the object to exist at runtime.
When and why would you use static with constexpr?
constexpr variables are not compile-time values
A value is immutable and does not occupy storage (it has no address),
however objects declared as constexpr
can be mutable and do occupy storage (under the as-if rule).
Mutability
Most objects declared as constexpr
are immutable,
but it is possible to define a constexpr
object that is (partially) mutable as follows:
struct S {
mutable int m;
};
int main() {
constexpr S s{42};
int arr[s.m]; // error: s.m is not a constant expression
s.m = 21; // ok, assigning to a mutable member of a const object
}
Storage
The compiler can, under the as-if rule, choose to not allocate any storage to store the value of an object declared as constexpr
.
Similarly, it can do such optimizations for non-constexpr variables.
However, consider the case where we need to pass the address of the object to a function that is not inlined; for example:
struct data {
int i;
double d;
// some more members
};
int my_algorithm(data const*, int);
int main() {
constexpr data precomputed = /*...*/;
int const i = /*run-time value*/;
my_algorithm(&precomputed, i);
}
The compiler here needs to allocate storage for precomputed
,
in order to pass its address to some non-inlined function.
It is possible for the compiler to allocate the storage for precomputed
and i
contiguously;
one could imagine situations where this might affect performance (see below).
Standardese
Variables are either objects or references [basic]/6.
Let's focus on objects.
A declaration like constexpr int a = 42;
is gramatically a simple-declaration;
it consists of decl-specifier-seq init-declarator-list ;
From [dcl.dcl]/9, we can conclude (but not rigorously) that such a declaration declares an object.
Specifically, we can (rigorously) conclude that it is an object declaration,
but this includes declarations of references.
See also the discussion of whether or not we can have variables of type void
.
The constexpr
in the declaration of an object implies that the object's type is const
[dcl.constexpr]/9.
An object is a region of storage[intro.object]/1.
We can infer from [intro.object]/6 and [intro.memory]/1 that every object has an address.
Note that we might not be able to directly take this address, e.g. if the object is referred to via a prvalue.
(There are even prvalues which are not objects, such as the literal 42
.)
Two distinct complete objects must have different addresses[intro.object]/6.
From this point, we can conclude that an object declared as constexpr
must have a unique address with respect to
any other (complete) object.
Furthermore, we can conclude that the declaration constexpr int a = 42;
declares an object with a unique address.
static and constexpr
The IMHO only interesting issue is the "per-function static
", à la
void foo() {
static constexpr int i = 42;
}
As far as I know -- but this seems still not entirely clear -- the compiler may compute the initializer of a constexpr
variable at run-time.
But this seems pathological; let's assume it does not do that,
i.e. it precomputes the initializer at compile-time.
The initialization of a static constexpr
local variable is done during static initializtion,
which must be performed before any dynamic initialization[basic.start.init]/2.
Although it is not guaranteed, we can probably assume that this does not impose a run-time/load-time cost.
Also, since there are no concurrency problems for constant initialization,
I think we can safely assume this does not require a thread-safe run-time check whether or not the static
variable has already been initialized.
(Looking into the sources of clang and gcc should shed some light on these issues.)
For the initialization of non-static local variables,
there are cases where the compiler cannot initialize the variable during constant initialization:
void non_inlined_function(int const*);
void recurse(int const i) {
constexpr int c = 42;
// a different address is guaranteed for `c` for each recursion step
non_inlined_function(&c);
if(i > 0) recurse(i-1);
}
int main() {
int i;
std::cin >> i;
recurse(i);
}
Conclusion
As it seems, we can benefit from static storage duration of a static constexpr
variable in some corner cases.
However, we might lose the locality of this local variable, as shown in the section "Storage" of this answer.
Until I see a benchmark that shows that this is a real effect,
I will assume that this is not relevant.
If there are only these two effects of static
on constexpr
objects,
I would use static
per default:
We typically do not need the guarantee of unique addresses for our constexpr
objects.
For mutable constexpr
objects (class types with mutable
members),
there are obviously different semantics between local static
and non-static constexpr
objects.
Similarly, if the value of the address itself is relevant (e.g. for a hash-map lookup).
Why constexpr should be static?
static
has a number of meanings. In classes (per your comment), it means that the member is associated with the class, and not a specific instance (object) of that class.
For a constexpr
, that makes a lot of sense. That's typically initialized by a value known to the compiler, and not from ctor arguments.
Difference between constexpr and static constexpr global variable
In your current example there is no difference: On variable declarations, constexpr
implies const
, and a const variable at namespace scope has internal linkage by default (so adding static
does not change anything).
In C++14, you cannot declare a variable as constexpr
and have it have external linkage unless you only ever do this in one single translation unit. The reason is that constexpr
variables require an initializer, and a declaration with initializer is a definition, and you must only have a single definition.
However, what you can do is use a normal integral constant, which you can declare (not define) as extern
, and in the translation unit where it is defined it can even be used as a constant expression:
lib.h:
extern const int a;
lib.cpp:
#include "lib.h"
const int a = 10;
int b[a] = {1, 2, 3}; // OK in this translation unit
In C++17, there is a new feature "inline variables" which lets you say:
inline constexpr int a = 10;
And this is an "inline definition" that can appear repeatedly, and each definition defines the same entity (just like all the other "inline" entities in the language).
constexpr vs. static const: Which one to prefer?
As long as we are talking about declaring compile-time constants of scalar integer or enum types, there's absolutely no difference between using const
(static const
in class scope) or constexpr
.
Note that compilers are required to support static const int
objects (declared with constant initializers) in constant expressions, meaning that they have no choice but to treat such objects as compile-time constants. Additionally, as long as such objects remain odr-unused, they require no definition, which further demonstrates that they won't be used as run-time values.
Also, rules of constant initialization prevent local static const int
objects from being initialized dynamically, meaning that there's no performance penalty for declaring such objects locally. Moreover, immunity of integral static
objects to ordering problems of static initialization is a very important feature of the language.
constexpr
is an extension and generalization of the concept that was originally implemented in C++ through const
with a constant initializer. For integer types constexpr
does not offer anything extra over what const
already did. constexpr
simply performs an early check of the "constness" of initializer. However, one might say that constexpr
is a feature designed specifically for that purpose so it fits better stylistically.
static constexpr vs constexpr in function body?
The main difference between those two declarations is the lifetime of the objects. When writing the question, I thought that using constexpr
instead of const
would place that object into the .rodata
section. But, I was wrong. The constexpr
keyword, here, only provides that the object can be used at compile-time functions. So, the object is actually created in the stack during run-time and destroyed when leaving the function's body.
On the other hand, the static constexpr
object is an object placed in the .rodata
section. It is created at the first time we call the wrapping function. In addition, thanks to the constexpr
, it is also available during compile-time.
Are constexpr functions implicitly static?
constexpr
functions are implicitly inline
.
inline
is a linking feature. An inline
function with definitions in different compilation units is not an error; if their definitions vary, your program is ill-formed no diagnostic required, but if they have the same definition then all but one version is discarded and that version is used.
static
, on a non-method function, is also a linking feature. A static
definition is not shared outside of its compilation unit; the compilation unit does not 'advertise' that it has a definition for isThree
.
static
on a method function has nothing to do with linking. In that case, it just means that this
is not implicitly passed to the function. A method with/without this
it doesn't work has differences, but they are mostly unrelated to them being constexpr
. Note that in at least c++14 a constexpr
method that doesn't use this
can still be constant evaluated. Some versions of c++ make constexpr
methods implicitly const
; c++17 does not.
&isThree
in one compilation unit and &isThree
in another can (and usually do) vary when static
(barring aggressive ICF, which is a matter for a different question). When inline
they may not vary.
inline
functions are shared between compilation units. Their full definition is also often visible in all compilation units aware of it, so it makes compiler "inlining" (as opposed to the keyword) your code easier. static
are not. constexpr
functions are implicitly inline
, but not implicitly static
.
Note that constexpr
functions can be evaluated in a runtime context sometimes. When evaluated in a compile time context, their inline
vs static
or linkage state really doesn't matter.
constexpr
means other things as well, but you wanted to know the difference between two different constexpr
declarations, and none of those meanings change.
What's the difference between static constexpr and static inline variables in C++17?
You don't have to specify an initializer for mySecondVar
at the point of declaration. Nor is the initializer required to be constexpr
itself.
This means that if we attempt to define myFirstVar
like this:
class MyClass {
static constexpr int myFirstVar;
};
int MyClass::myFirstVar = 1;
Or like this:
#include <cstdlib>
class MyClass {
static constexpr int myFirstVar = rand();
};
It's ill-formed either way. constexpr
semantics demand it and for a good reason.
The inline
specifier approach allows us to include a static variable definition in the header itself, without the initializer being constexpr
; or if the initializer is fairly complex it doesn't have to be in the class definition itself.
So this is a perfectly valid header in C++17:
#include <cstdlib>
class MyClass {
static const int mySecondVar;
};
inline const int MyClass::mySecondVar = rand();
The standard promises us that all translation units that include the header will see the same value for the variable, even though we won't know what it is until run-time.
It's mostly a library writers tool. Assume your library is header only. Then in the olden days, what were your options if you needed a static constant defined like this?
Well, you could have an object file shipped with your library. It will be compiled from a translation unit that contains just the constant definition. Now the library isn't header-only.
Or you could rely on inline functions instead. The inline variable effect can be achieved with the following:
class MyClass {
static inline int mySecondVar();
};
inline int MyClass::mySecondVar() {
static const int value = rand();
return value;
}
But it's hidden behind a wall of syntax, and masks what is essentially a constant, with a function call operator.
Related Topics
Gcc Error: Explicit Specialization in Non-Namespace Scope
How Do Static Variables in Lambda Function Objects Work
Using a Static Library in Qt Creator
C++ Most Efficient Way to Convert String to Int (Faster Than Atoi)
How to Write a Stateful Allocator in C++11, Given Requirements on Copy Construction
Why Is the Destructor Call After the Std::Move Necessary
How to Make a Variadic Is_Same
Variable Length Arrays (Vla) in C and C++
Where Does One Get the "Sys/Socket.H" Header/Source File
How to Wait on Multiple Condition Variables in C++11
How to Enable /Std:C++17 in VS2017 with Cmake
What Does _T Stands for in a Cstring
Static_Assert on Initializer_List::Size()
How to Get Cmake to Recognize Pthread on Ubuntu
Name of Process for Active Window in Windows 8/10