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.
What's the difference between constexpr and const?
Basic meaning and syntax
Both keywords can be used in the declaration of objects as well as functions. The basic difference when applied to objects is this:
const
declares an object as constant. This implies a guarantee that once initialized, the value of that object won't change, and the compiler can make use of this fact for optimizations. It also helps prevent the programmer from writing code that modifies objects that were not meant to be modified after initialization.constexpr
declares an object as fit for use in what the Standard calls constant expressions. But note thatconstexpr
is not the only way to do this.
When applied to functions the basic difference is this:
const
can only be used for non-static member functions, not functions in general. It gives a guarantee that the member function does not modify any of the non-static data members (except for mutable data members, which can be modified anyway).constexpr
can be used with both member and non-member functions, as well as constructors. It declares the function fit for use in constant expressions. The compiler will only accept it if the function meets certain criteria (7.1.5/3,4), most importantly (†):- The function body must be non-virtual and extremely simple: Apart from typedefs and static asserts, only a single
return
statement is allowed. In the case of a constructor, only an initialization list, typedefs, and static assert are allowed. (= default
and= delete
are allowed, too, though.) - As of C++14, the rules are more relaxed, what is allowed since then inside a constexpr function:
asm
declaration, agoto
statement, a statement with a label other thancase
anddefault
, try-block, the definition of a variable of non-literal type, definition of a variable of static or thread storage duration, the definition of a variable for which no initialization is performed. - The arguments and the return type must be literal types (i.e., generally speaking, very simple types, typically scalars or aggregates)
- The function body must be non-virtual and extremely simple: Apart from typedefs and static asserts, only a single
Constant expressions
As said above, constexpr
declares both objects as well as functions as fit for use in constant expressions. A constant expression is more than merely constant:
It can be used in places that require compile-time evaluation, for example, template parameters and array-size specifiers:
template<int N>
class fixed_size_list
{ /*...*/ };
fixed_size_list<X> mylist; // X must be an integer constant expression
int numbers[X]; // X must be an integer constant expressionBut note:
Declaring something as
constexpr
does not necessarily guarantee that it will be evaluated at compile time. It can be used for such, but it can be used in other places that are evaluated at run-time, as well.An object may be fit for use in constant expressions without being declared
constexpr
. Example:int main()
{
const int N = 3;
int numbers[N] = {1, 2, 3}; // N is constant expression
}This is possible because
N
, being constant and initialized at declaration time with a literal, satisfies the criteria for a constant expression, even if it isn't declaredconstexpr
.
So when do I actually have to use constexpr
?
- An object like
N
above can be used as constant expression without being declaredconstexpr
. This is true for all objects that are: const
- of integral or enumeration type and
- initialized at declaration time with an expression that is itself a constant expression
[This is due to §5.19/2: A constant expression must not include a subexpression that involves "an lvalue-to-rvalue modification unless […] a glvalue of integral or enumeration type […]" Thanks to Richard Smith for correcting my earlier claim that this was true for all literal types.]
For a function to be fit for use in constant expressions, it must be explicitly declared
constexpr
; it is not sufficient for it merely to satisfy the criteria for constant-expression functions. Example:template<int N>
class list
{ };
constexpr int sqr1(int arg)
{ return arg * arg; }
int sqr2(int arg)
{ return arg * arg; }
int main()
{
const int X = 2;
list<sqr1(X)> mylist1; // OK: sqr1 is constexpr
list<sqr2(X)> mylist2; // wrong: sqr2 is not constexpr
}
When can I / should I use both, const
and constexpr
together?
A. In object declarations. This is never necessary when both keywords refer to the same object to be declared. constexpr
implies const
.
constexpr const int N = 5;
is the same as
constexpr int N = 5;
However, note that there may be situations when the keywords each refer to different parts of the declaration:
static constexpr int N = 3;
int main()
{
constexpr const int *NP = &N;
}
Here, NP
is declared as an address constant-expression, i.e. a pointer that is itself a constant expression. (This is possible when the address is generated by applying the address operator to a static/global constant expression.) Here, both constexpr
and const
are required: constexpr
always refers to the expression being declared (here NP
), while const
refers to int
(it declares a pointer-to-const). Removing the const
would render the expression illegal (because (a) a pointer to a non-const object cannot be a constant expression, and (b) &N
is in-fact a pointer-to-constant).
B. In member function declarations. In C++11, constexpr
implies const
, while in C++14 and C++17 that is not the case. A member function declared under C++11 as
constexpr void f();
needs to be declared as
constexpr void f() const;
under C++14 in order to still be usable as a const
function.
Which should I prefer for a constant within a function: constexpr const or enum?
You can accidentally or on purpose force ODR-existence of bar
if it was a constexpr int bar = 456;
, this is not possible with enum : int { bar = 456 };
.
This may or may not be an advantage on either side.
For example
int baz(int const* ptr ) {
if (ptr) return 7; return -1;
}
int foo(int x)
{
// enum : int { bar = 456 };
constexpr int bar = 456;
return x + baz(&bar);
}
the enum
version doesn't compile, the constexpr int
one does. A constexpr int
can be an lvalue, an enumerator (one of the listed enum constants) cannot.
The enum values aren't actually an int
, while the constexpr int
is actually an int
. This may matter if you pass it to
template<class T>
void test(T) {
static_assert(std::is_same<T,int>::value);
}
one will pass the test; the other will not.
Again, this could be an advantage, a disadvantage, or a meaningless quirk depending on how you are using the token.
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).
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).
What is the difference between a static const and constexpr variable?
The main difference that I know is, the value of constexpr
must be known in compile-time while a const static
can be assigned in run-time.
const static int x = rand();
Replacing constants: when to use static constexpr and inline constexpr?
In C++17, the proper way to replace those old idioms (e.g. #define
) in headers in namespace scope is to use constexpr inline
variables -- and not static
(which is implied: they already have internal linkage).
While typically you won't encounter ODR issues (because integer compile-time constants such as those you describe are rarely ODR-used and there is a provision for their typical usage within inline
functions), it is best to mark them as inline
now that we have the feature in the language and avoid all problems.
See Should `const` and `constexpr` variables in headers be `inline` to prevent ODR violations? for the technical details about it.
Related Topics
What Is the Default Hash Function Used in C++ Std::Unordered_Map
When Should Your Destructor Be Virtual
Why Is 'Std::Initializer_List' Often Passed by Value
Practical Uses for the "Curiously Recurring Template Pattern"
Using Hierarchy in Findcontours () in Opencv
How to Create a Dll with Swig from Visual Studio 2010
What Is the Purpose of _Cxa_Pure_Virtual
C++11 Way to Index Tuple at Runtime Without Using Switch
Initializing Default Values in a Struct
Why Is Statically Linking Glibc Discouraged
How to Reduce Compile Time, and Linking Time for Visual C++ Projects (Native C++)
Difference Between Using #Include<Filename> and #Include<Filename.H> in C++
Casting Pointer to Array (Int* to Int[2])
Getting the MAChine Serial Number and CPU Id Using C/C++ in Linux
How to Define a String Literal in Gcc Command Line
Avoiding Denormal Values in C++
Why Was 1 << 31 Changed to Be Implementation-Defined in C++14