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.
constexpr const vs constexpr variables?
The issue is that in a variable declaration, constexpr
always applies the const
-ness to the object declared; const
on the other hand can apply to a different type, depending on the placement.
Thus
constexpr const int i = 3;
constexpr int i = 3;
are equivalent;
constexpr char* p = nullptr;
constexpr char* const p = nullptr;
are equivalent; both make p
a const
pointer to char
.
constexpr const char* p = nullptr;
constexpr const char* const p = nullptr;
are equivalent. constexpr
makes p
a const
pointer. The const
in const char *
makes p
point to const char
.
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).
const vs constexpr on variables
I believe there is a difference. Let's rename them so that we can talk about them more easily:
const double PI1 = 3.141592653589793;
constexpr double PI2 = 3.141592653589793;
Both PI1
and PI2
are constant, meaning you can not modify them. However only PI2
is a compile-time constant. It shall be initialized at compile time. PI1
may be initialized at compile time or run time. Furthermore, only PI2
can be used in a context that requires a compile-time constant. For example:
constexpr double PI3 = PI1; // error
but:
constexpr double PI3 = PI2; // ok
and:
static_assert(PI1 == 3.141592653589793, ""); // error
but:
static_assert(PI2 == 3.141592653589793, ""); // ok
As to which you should use? Use whichever meets your needs. Do you want to ensure that you have a compile time constant that can be used in contexts where a compile-time constant is required? Do you want to be able to initialize it with a computation done at run time? Etc.
Difference between const and constexpr arrays
A longer comment as community wiki.
The expression xs[0]
is defined in [expr.sub]/1 as *((xs)+(0))
. (See below for the parantheses.)
One of the expressions shall have the type “pointer to
T
” and the other shall have unscoped enumeration or integral type.
Therefore, the array-to-pointer conversion [conv.array] is applied:
An lvalue or rvalue of type “array of
N T
” or “array of unknown bound ofT
” can be converted to a prvalue of type “pointer toT
”. The result is a pointer to the first element of the array.
Note it can operate on an lvalue and the result is a prvalue, 0
as an integer literal is a prvalue as well. The addition is defined in [expr.add]/5. As both are prvalues, no lvalue-to-rvalue conversion is required.
int arr[3];
constexpr int* p = arr; // allowed and compiles
The crucial step now seems to be the indirection *
[expr.unary.op]/1
The unary
*
operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points.
So, the result of xs[0]
is an lvalue referring to the first element of the xs
array, and is of type int const
.
N.B. [expr.prim.general]/6
A parenthesized expression is a primary expression whose type and value are identical to those of the enclosed expression. The presence of parentheses does not affect whether the expression is an lvalue.
If we now look at the bullets in [expr.const]/2 which disallow certain expressions and conversions to appear in constant expressions, the only bullet that could apply (AFAIK) is the lvalue-to-rvalue conversion:
an lvalue-to-rvalue conversion (4.1) unless it is applied to
a non-volatile glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization, initialized with a constant expression [Note: a string literal (2.14.5) corresponds to an array of such objects. —end note ], or
a non-volatile glvalue of literal type that refers to a non-volatile object defined with
constexpr
, or that refers to a sub-object of such an object, or
[...]
But the only true lvalue-to-rvalue conversion as per (4.1) (not 4.2, which is array-to-pointer) that appears in the evaluation of xs[0]
is the conversion from the resulting lvalue referring to the first element.
For the example in the OP:
int const xs[]{1, 2, 3};
int as[xs[0]]; // error.
This element xs[0]
has non-volatile const integral type, its initialization precedes the constant expression where it occurs, and it has been initialized with a constant expression.
By the way, the added "Note" in the quoted passage of [expr.const]/2 has been added to clarify that this is legal:
constexpr char c = "hello"[0];
Note that a string literal is an lvalue as well.
It would be great if someone (could change this to) explain why xs[0]
is not allowed to appear in a constant expression.
What's the real difference between constinit and constexpr? [duplicate]
A constinit
variable is constant initialized, but it is not usable in a constant expression, nor even automatically constant. Your main
can legally contain this line
c2 = 2;
Yup, modification is possible after initialization.
Is `const constexpr` on variables redundant?
In declarations with primitives, such as the one in your example, const
is indeed redundant. However, there may be odd situations where const
would be required, for example
constexpr int someConstant = 3;
constexpr const int *someConstantPointerToConstant = &someConstant;
Here, someConstantPointerToConstant
is both a constexpr
(i.e. it's known at compile time, hence constexpr
) and it is also a pointer to constant (i.e. its object cannot be changed, hence const
). The second declaration above would not compile with const
omitted (demo).
Difference between `constexpr` and `#define`
Statements defined using #define
are called macros. And macros are used in a multitude of uses.
- We can use them to conditionally compile sections of code.
#ifdef ONE
int AddOne(int x) { return x + 1; }
#else
int AddTwo(int x) { return x + 2; }
#endif
- When we don't need to store constants in a variable.
#define MAX_BOUND 1000
#define MIN_BOUND 10
- For places where we can use a macros to change the type of data.
#ifdef USE_WIDE_CHAR
#define TEXT(...) L##__VA_ARGS__
#else
#define TEXT(...) __VA_ARGS__
#endif
- To define keywords based on a condition.
#ifdef BUILD_DLL
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif
Since they are resolved before the actual compilation phase, we can make small improvement to the source code depending on certain factors (platforms, build systems, architecture, etc...).
constexpr
essentially states that a variable or function can be resolved at compile time, but is not guaranteed.
I feel like constexpr is just a #define where the type can be chosen.
Its not completely true. As I stated before, because they are resolved before the compilation phase, we can take some advantages of it. The only common use would be for constant values which the compiler could easily replace as an optimization. Apart from that, the use cases are different.
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.
Does constexpr really imply const? [duplicate]
Does constexpr really imply const?
Yes. Constexpr variables are always const.
What is the difference?
const T*
is a non-const pointer to const. It is not const.
T* const
is a const pointer to non-const. It is const.
const T* const
is a const pointer to const. It is const.
constexpr T*
is a const pointer to non-const. It is const by virtue of being constexpr.
const constexpr T*
is a const pointer to const. It is const by virtue of being constexpr.
String literals are arrays of const char, and they don't implicitly convert to a pointer to non-const char (since C++11).
Related Topics
What Are the Advantages of List Initialization (Using Curly Braces)
Sorting a Vector of Custom Objects
Why Should I Prefer to Use Member Initialization Lists
What Is the Type of String Literals in C and C++
What Is a "Translation Unit" in C++
Why Does Dividing Two Int Not Yield the Right Value When Assigned to Double
Calling Virtual Functions Inside Constructors
Can Num++ Be Atomic For 'Int Num'
When Does Invoking a Member Function on a Null Instance Result in Undefined Behavior
Erasing Elements from a Vector
How to Parse a String to an Int in C++
Definitive List of Common Reasons For Segmentation Faults
C++ Delete - It Deletes My Objects But I Can Still Access the Data
Invalid Operands Error for Addition and Integer Division
How to Properly Clean Up Elements from Vectors of Object Pointers