What's the difference between constexpr and const?
When can I use only one of them?
When can I use both and how should I choose one?
Best Solution
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 that constexpr 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, a goto statement, a statement with a label other than case and default, 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)
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 expression
But 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 declared constexpr.
So when do I actually have to use constexpr?
An object like N above can be used as constant expression without being declared constexpr. 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 constexprtogether?
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.
In practice, the difference is in the location where the preprocessor searches for the included file.
For #include <filename> the preprocessor searches in an implementation dependent manner, normally in search directories pre-designated by the compiler/IDE. This method is normally used to include standard library header files.
For #include "filename" the preprocessor searches first in the same directory as the file containing the directive, and then follows the search path used for the #include <filename> form. This method is normally used to include programmer-defined header files.
having to declare the value at the time of a definition for a const VS readonly values can be computed dynamically but need to be assigned before the constructor exits.. after that it is frozen.
const's are implicitly static. You use a ClassName.ConstantName notation to access them.
There is a subtle difference. Consider a class defined in AssemblyA.
public class Const_V_Readonly
{
public const int I_CONST_VALUE = 2;
public readonly int I_RO_VALUE;
public Const_V_Readonly()
{
I_RO_VALUE = 3;
}
}
AssemblyB references AssemblyA and uses these values in code. When this is compiled:
in the case of the const value, it is like a find-replace. The value 2 is 'baked into' the AssemblyB's IL. This means that if tomorrow I update I_CONST_VALUE to 20, AssemblyB would still have 2 till I recompile it.
in the case of the readonly value, it is like a ref to a memory location. The value is not baked into AssemblyB's IL. This means that if the memory location is updated, AssemblyB gets the new value without recompilation. So if I_RO_VALUE is updated to 30, you only need to build AssemblyA and all clients do not need to be recompiled.
So if you are confident that the value of the constant won't change, use a const.
public const int CM_IN_A_METER = 100;
But if you have a constant that may change (e.g. w.r.t. precision).. or when in doubt, use a readonly.
public readonly float PI = 3.14;
Update: Aku needs to get a mention because he pointed this out first. Also I need to plug where I learned this: Effective C# - Bill Wagner
Best Solution
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 (†):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.)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.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:
But 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: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
?N
above can be used as constant expression without being declaredconstexpr
. This is true for all objects that are:const
[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:When can I / should I use both,
const
andconstexpr
together?A. In object declarations. This is never necessary when both keywords refer to the same object to be declared.
constexpr
impliesconst
.is the same as
However, note that there may be situations when the keywords each refer to different parts of the declaration:
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, bothconstexpr
andconst
are required:constexpr
always refers to the expression being declared (hereNP
), whileconst
refers toint
(it declares a pointer-to-const). Removing theconst
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
impliesconst
, while in C++14 and C++17 that is not the case. A member function declared under C++11 asneeds to be declared as
under C++14 in order to still be usable as a
const
function.