Difference between revisions of "cpp/language/constexpr"
(Applied P2448R2 (Relaxing some constexpr restrictions) part 1.) |
m (tt title's keyword) |
||
(28 intermediate revisions by 10 users not shown) | |||
Line 1: | Line 1: | ||
− | {{title|constexpr specifier {{mark since c++11}}}} | + | {{title|{{tt|constexpr}} specifier {{mark since c++11}}}} |
{{cpp/language/declarations/navbar}} | {{cpp/language/declarations/navbar}} | ||
:*{{ttb|constexpr}} - specifies that the value of a variable or function can appear in {{rlp|constant expression}}s | :*{{ttb|constexpr}} - specifies that the value of a variable or function can appear in {{rlp|constant expression}}s | ||
===Explanation=== | ===Explanation=== | ||
+ | The {{c/core|constexpr}} specifier declares that it is possible to evaluate the value of the function or variable at compile time. Such variables and functions can then be used where only compile time {{rlp|constant expression}}s are allowed (provided that appropriate function arguments are given). | ||
− | + | A {{c/core|constexpr}} specifier used in an object declaration {{rev inl|id=constexpr-method-is-const|until=c++14| or non-static member function}} implies {{c/core|const}}. A {{c/core|constexpr}} specifier used in a function {{rev inl|since=c++17| or {{rlp|static}} data member}} declaration implies {{c/core|inline}}. If any declaration of a function or function template has a {{c/core|constexpr}} specifier, then every declaration must contain that specifier. | |
− | + | {{anchor|constexpr variable}} | |
+ | ===={{c/core|constexpr}} variable==== | ||
+ | A '''{{c/core|constexpr}} variable''' must satisfy the following requirements: | ||
− | + | :* its type must be a {{named req|LiteralType}} | |
− | + | ||
− | :* its type must be a {{named req|LiteralType}} | + | |
:* it must be immediately initialized | :* it must be immediately initialized | ||
:* the {{rlp|eval order|full-expression}} of its initialization, including all implicit conversions, constructors calls, etc, must be a {{rlp|constant expression}} | :* the {{rlp|eval order|full-expression}} of its initialization, including all implicit conversions, constructors calls, etc, must be a {{rlp|constant expression}} | ||
Line 19: | Line 20: | ||
::* it is of class type or (possibly multi-dimensional) array thereof, that class type has a constexpr destructor, and for a hypothetical expression {{c|e}} whose only effect is to destroy the object, {{c|e}} would be a core constant expression if the lifetime of the object and its non-mutable subobjects (but not its mutable subobjects) were considered to start within {{c|e}}. | ::* it is of class type or (possibly multi-dimensional) array thereof, that class type has a constexpr destructor, and for a hypothetical expression {{c|e}} whose only effect is to destroy the object, {{c|e}} would be a core constant expression if the lifetime of the object and its non-mutable subobjects (but not its mutable subobjects) were considered to start within {{c|e}}. | ||
− | If a {{ | + | If a {{c/core|constexpr}} variable is not {{rlp|tu local|translation-unit-local}}, it should not be initialized to refer to a translation-unit-local entity that is usable in constant expressions, nor have a subobject that refers to such an entity. Such initialization is disallowed in a {{rlp|modules|module interface unit}} (outside its private-module-fragment, if any) or a module partition, and is deprecated in any other context. |
}} | }} | ||
− | A '''{{ | + | {{anchor|constexpr function}} |
+ | ===={{c/core|constexpr}} function==== | ||
+ | A '''{{c/core|constexpr}} function''' must satisfy the following requirements: | ||
{{rrev multi|until1=c++20|rev1= | {{rrev multi|until1=c++20|rev1= | ||
:* it must not be {{rlp|virtual}} | :* it must not be {{rlp|virtual}} | ||
− | :* it must not be a {{rlp|function | + | :* it must not be a {{rlp|try#Function try block|function {{c/core|try}} block}} |
|rev2= | |rev2= | ||
:* it must not be a {{rlp|coroutines|coroutine}}}} | :* it must not be a {{rlp|coroutines|coroutine}}}} | ||
Line 32: | Line 35: | ||
{{rrev|until=c++23| | {{rrev|until=c++23| | ||
:* its return value (if any) and each of its parameters must be of a {{named req|LiteralType}} | :* its return value (if any) and each of its parameters must be of a {{named req|LiteralType}} | ||
− | :* there exists at least one set of argument values such that an invocation of the function could be an evaluated subexpression of a {{rlp|constant expression#Core constant expressions|core constant expression}} | + | :* there exists at least one set of argument values such that an invocation of the function could be an evaluated subexpression of a {{rlp|constant expression#Core constant expressions|core constant expression}} (for constructors, use in a {{rlp|constant initialization|constant initializer}} is sufficient). No diagnostic is required for a violation of this bullet. |
}} | }} | ||
− | |||
{{rrev multi | {{rrev multi | ||
Line 48: | Line 50: | ||
:* <span id="relaxed-constexpr">the function body must '''not''' contain</span>: | :* <span id="relaxed-constexpr">the function body must '''not''' contain</span>: | ||
::* a {{rlp|goto}} statement | ::* a {{rlp|goto}} statement | ||
− | ::* a statement with a {{rlp|statements# | + | ::* a statement with a {{rlp|statements#Labeled statements|label}} other than {{c/core|case}} and {{c/core|default}} |
{{rrev|until=c++20| | {{rrev|until=c++20| | ||
− | ::* a {{rlp|try | + | ::* a {{rlp|try|{{c/core|try}} block}} |
::* an {{rlp|asm|asm declaration}} | ::* an {{rlp|asm|asm declaration}} | ||
::* a definition of a variable for which {{rlp|default initialization|no initialization is performed}} | ::* a definition of a variable for which {{rlp|default initialization|no initialization is performed}} | ||
Line 56: | Line 58: | ||
::* a definition of a variable of non-literal type | ::* a definition of a variable of non-literal type | ||
::* a definition of a variable of static or thread {{rlp|storage duration}} | ::* a definition of a variable of static or thread {{rlp|storage duration}} | ||
− | :: (A function body that is {{c|1==default;}} or {{c|1==delete;}} contains none of the above.) | + | :: (A function body that is {{c|1== default;}} or {{c|1== delete;}} contains none of the above.) |
}} | }} | ||
− | |||
{{rrev|until=c++23| | {{rrev|until=c++23| | ||
{{anchor|constexpr constructor}} | {{anchor|constexpr constructor}} | ||
− | A '''{{ | + | ===={{c/core|constexpr}} constructor==== |
+ | A '''{{c/core|constexpr}} constructor''' whose function body is not {{c|1== delete;}} must satisfy the following additional requirements: | ||
{{rrev|until=c++20| | {{rrev|until=c++20| | ||
:* for the constructor of a {{rlp|class|class or struct}}, every base class sub-object and every {{rlp|union#Union-like classes|non-variant}} non-static data member must be initialized. If the class is a {{rlp|union#Union-like classes|union-like class}}, for each of its non-empty anonymous union members, exactly one variant member must be initialized | :* for the constructor of a {{rlp|class|class or struct}}, every base class sub-object and every {{rlp|union#Union-like classes|non-variant}} non-static data member must be initialized. If the class is a {{rlp|union#Union-like classes|union-like class}}, for each of its non-empty anonymous union members, exactly one variant member must be initialized | ||
Line 68: | Line 70: | ||
}} | }} | ||
:* every constructor selected to initializing non-static data members and base class must be a constexpr constructor. | :* every constructor selected to initializing non-static data members and base class must be a constexpr constructor. | ||
− | |||
− | |||
− | |||
− | |||
{{anchor|constexpr destructor}} | {{anchor|constexpr destructor}} | ||
− | Destructors cannot be {{ | + | ===={{c/core|constexpr}} destructor==== |
+ | {{rrev multi|until1=c++20 | ||
+ | |rev1= | ||
+ | Destructors cannot be {{c/core|constexpr}}, but a {{rlp|destructor#Trivial destructor|trivial destructor}} can be implicitly called in constant expressions. | ||
|rev2= | |rev2= | ||
− | + | A '''{{c/core|constexpr}} destructor''' whose function body is not {{c|1== delete;}} must satisfy the following additional requirement: | |
− | A '''{{ | + | |
:* every destructor used to destroy non-static data members and base class must be a constexpr destructor. | :* every destructor used to destroy non-static data members and base class must be a constexpr destructor. | ||
+ | }} | ||
}} | }} | ||
− | For constexpr function templates and constexpr member functions of class templates, at least one specialization must satisfy the abovementioned requirements. Other specializations are still considered as constexpr, even though a call to such a function cannot appear in a constant expression.<!-- e.g. template<class T> struct X { constexpr X(T); }; X<string>::X(string) is a constexpr constructor, so X<string> is a literal type. --> | + | For constexpr function templates and constexpr member functions of class templates, at least one specialization must satisfy the abovementioned requirements. Other specializations are still considered as constexpr, even though a call to such a function cannot appear in a constant expression. |
+ | |||
+ | {{rrev|until=c++23| | ||
+ | If no specialization of the template would satisfy the requirements for a constexpr function when considered as a non-template function, the template is ill-formed, no diagnostic required.<!--e.g. template<class T> struct X { constexpr X(T); }; X<string>::X(string) is a constexpr constructor, so X<string> is a literal type.--> | ||
+ | }} | ||
+ | {{rrev|since=c++20| | ||
+ | A constexpr function implicitly becomes an {{rlp|consteval|immediate function}} if it is not marked {{c/core|consteval}}, uses an immediate function in a non-constant manner, and is | ||
+ | * a call operator of a {{rlp|lambda}}, or | ||
+ | * a defaulted special member function, or | ||
+ | * a specialization of a templated entity marked {{c/core|constexpr}}. | ||
+ | }} | ||
===Notes=== | ===Notes=== | ||
{{rrev|until=c++17| | {{rrev|until=c++17| | ||
− | Because the {{ | + | Because the {{rlpt|noexcept}} operator always returns {{c|true}} for a constant expression, it can be used to check if a particular invocation of a constexpr function takes the constant expression branch: |
{{source|1= | {{source|1= | ||
constexpr int f(); | constexpr int f(); | ||
Line 94: | Line 105: | ||
}} | }} | ||
− | Constexpr constructors are permitted for classes that aren't literal types. For example, the default constructor of {{lc|std:: | + | {{rrev|since=c++23| |
+ | It is possible to write a constexpr function whose invocation can never satisfy the requirements of a core constant expression: | ||
+ | |||
+ | {{source|1= | ||
+ | void f(int& i) // not a constexpr function | ||
+ | { | ||
+ | i = 0; | ||
+ | } | ||
+ | |||
+ | constexpr void g(int& i) // well-formed since C++23 | ||
+ | { | ||
+ | f(i); // unconditionally calls f, cannot be a constant expression | ||
+ | } | ||
+ | }} | ||
+ | }} | ||
+ | |||
+ | Constexpr constructors are permitted for classes that aren't literal types. For example, the default constructor of {{lc|std::shared_ptr}} is constexpr, allowing {{rlp|constant initialization}}. | ||
Reference variables can be declared constexpr (their initializers have to be {{rlp|constant expression#Constant expression|reference constant expressions}}): | Reference variables can be declared constexpr (their initializers have to be {{rlp|constant expression#Constant expression|reference constant expressions}}): | ||
Line 104: | Line 131: | ||
{{rrev|since=c++20|1= | {{rrev|since=c++20|1= | ||
− | Even though {{c|try}} blocks and inline assembly are allowed in constexpr functions, throwing exceptions or executing the assembly is still disallowed in a constant expression. | + | Even though {{c/core|try}} blocks and inline assembly are allowed in constexpr functions, throwing exceptions or executing the assembly is still disallowed in a constant expression. |
If a variable has constant destruction, there is no need to generate machine code in order to call destructor for it, even if its destructor is not trivial. | If a variable has constant destruction, there is no need to generate machine code in order to call destructor for it, even if its destructor is not trivial. | ||
+ | |||
+ | A non-lambda, non-special-member, and non-templated constexpr function cannot implicitly become an immediate function. Users need to explicitly mark it {{c/core|consteval}} to make such an intended function definition well-formed. | ||
}} | }} | ||
{{ftm begin|core=1|std=1|comment=1|sort=1}} | {{ftm begin|core=1|std=1|comment=1|sort=1}} | ||
− | {{ftm|value=200704L|std=C++11|__cpp_constexpr|rowspan=" | + | {{ftm|value=200704L|std=C++11|__cpp_constexpr|rowspan="9"|{{c/core|constexpr}}}} |
− | {{ftm|value=201304L|std=C++14|-|[[#relaxed-constexpr|Relaxed {{ | + | {{ftm|value=201304L|std=C++14|-|[[#relaxed-constexpr|Relaxed {{c/core|constexpr}}]], [[#constexpr-method-is-const|non-{{c/core|const}} {{c/core|constexpr}} methods]]}} |
{{ftm|value=201603L|std=C++17|-|[[cpp/language/lambda|Constexpr lambda]]}} | {{ftm|value=201603L|std=C++17|-|[[cpp/language/lambda|Constexpr lambda]]}} | ||
− | {{ftm|value=201907L|std=C++20|-|Trivial {{rlp|default initialization}} and {{rlp|asm|asm-declaration}} in {{ | + | {{ftm|value=201907L|std=C++20|-|Trivial {{rlp|default initialization}} and {{rlp|asm|asm-declaration}} in {{c/core|constexpr}} functions}} |
{{ftm|value=202002L|std=C++20|-|Changing the active member of a union in constant evaluation}} | {{ftm|value=202002L|std=C++20|-|Changing the active member of a union in constant evaluation}} | ||
{{ftm|value=202110L|std=C++23|-|Non-[[cpp/named req/LiteralType|literal]] variables, labels, and {{ltt|cpp/language|goto}} statements in constexpr functions}} | {{ftm|value=202110L|std=C++23|-|Non-[[cpp/named req/LiteralType|literal]] variables, labels, and {{ltt|cpp/language|goto}} statements in constexpr functions}} | ||
− | {{ftm|value=202207L|std=C++23|-|Relaxing some {{c|constexpr}} restrictions}} | + | {{ftm|value=202207L|std=C++23|-|Relaxing some {{c/core|constexpr}} restrictions}} |
+ | {{ftm|value=202211L|std=C++23|-|Permitting {{c/core|static}} {{c/core|constexpr}} variables in {{c/core|constexpr}} functions}} | ||
+ | {{ftm|value=202306L|std=C++26|-|Constexpr cast from {{c/core|void*}}: towards constexpr type-erasure}} | ||
{{ftm|value=201711L|std=C++11)<br>(DR|__cpp_constexpr_in_decltype|Generation of function and variable definitions when [[cpp/language/constant expression#Functions and variables needed for constant evaluation|needed for constant evaluation]]}} | {{ftm|value=201711L|std=C++11)<br>(DR|__cpp_constexpr_in_decltype|Generation of function and variable definitions when [[cpp/language/constant expression#Functions and variables needed for constant evaluation|needed for constant evaluation]]}} | ||
− | {{ftm|value=201907L|std=C++20|__cpp_constexpr_dynamic_alloc|Operations for dynamic storage duration in {{c|constexpr}} functions}} | + | {{ftm|value=201907L|std=C++20|__cpp_constexpr_dynamic_alloc|Operations for dynamic storage duration in {{c/core|constexpr}} functions}} |
{{ftm end}} | {{ftm end}} | ||
Line 124: | Line 155: | ||
===Example=== | ===Example=== | ||
− | {{example | + | {{example |
− | + | |Defines C++11/14 {{c/core|constexpr}} functions that compute factorials; defines a literal type that extends string literals: | |
|code= | |code= | ||
#include <iostream> | #include <iostream> | ||
Line 131: | Line 162: | ||
// C++11 constexpr functions use recursion rather than iteration | // C++11 constexpr functions use recursion rather than iteration | ||
− | |||
constexpr int factorial(int n) | constexpr int factorial(int n) | ||
{ | { | ||
Line 137: | Line 167: | ||
} | } | ||
− | // literal class | + | // C++14 constexpr functions may use local variables and loops |
+ | #if __cplusplus >= 201402L | ||
+ | constexpr int factorial_cxx14(int n) | ||
+ | { | ||
+ | int res = 1; | ||
+ | while (n > 1) | ||
+ | res *= n--; | ||
+ | return res; | ||
+ | } | ||
+ | #endif // C++14 | ||
+ | |||
+ | // A literal class | ||
class conststr | class conststr | ||
{ | { | ||
Line 145: | Line 186: | ||
template<std::size_t N> | template<std::size_t N> | ||
constexpr conststr(const char(&a)[N]): p(a), sz(N - 1) {} | constexpr conststr(const char(&a)[N]): p(a), sz(N - 1) {} | ||
− | + | ||
// constexpr functions signal errors by throwing exceptions | // constexpr functions signal errors by throwing exceptions | ||
// in C++11, they must do so from the conditional operator ?: | // in C++11, they must do so from the conditional operator ?: | ||
Line 152: | Line 193: | ||
return n < sz ? p[n] : throw std::out_of_range(""); | return n < sz ? p[n] : throw std::out_of_range(""); | ||
} | } | ||
− | + | ||
constexpr std::size_t size() const { return sz; } | constexpr std::size_t size() const { return sz; } | ||
}; | }; | ||
// C++11 constexpr functions had to put everything in a single return statement | // C++11 constexpr functions had to put everything in a single return statement | ||
− | // (C++14 | + | // (C++14 does not have that requirement) |
constexpr std::size_t countlower(conststr s, std::size_t n = 0, | constexpr std::size_t countlower(conststr s, std::size_t n = 0, | ||
std::size_t c = 0) | std::size_t c = 0) | ||
{ | { | ||
return n == s.size() ? c : | return n == s.size() ? c : | ||
− | 'a' <= s[n] && s[n] <= 'z' ? countlower(s, n + 1, c + 1) | + | 'a' <= s[n] && s[n] <= 'z' ? countlower(s, n + 1, c + 1) |
− | + | : countlower(s, n + 1, c); | |
} | } | ||
− | // output function that requires a compile-time constant, for testing | + | // An output function that requires a compile-time constant, for testing |
template<int n> | template<int n> | ||
struct constN | struct constN | ||
Line 175: | Line 216: | ||
int main() | int main() | ||
{ | { | ||
− | std::cout << "4! = " ; | + | std::cout << "4! = "; |
constN<factorial(4)> out1; // computed at compile time | constN<factorial(4)> out1; // computed at compile time | ||
Line 181: | Line 222: | ||
std::cout << k << "! = " << factorial(k) << '\n'; // computed at run time | std::cout << k << "! = " << factorial(k) << '\n'; // computed at run time | ||
− | std::cout << " | + | std::cout << "The number of lowercase letters in \"Hello, world!\" is "; |
constN<countlower("Hello, world!")> out2; // implicitly converted to conststr | constN<countlower("Hello, world!")> out2; // implicitly converted to conststr | ||
− | + | ||
constexpr int a[12] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; | constexpr int a[12] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; | ||
− | constexpr int length_a = sizeof | + | constexpr int length_a = sizeof a / sizeof(int); // std::size(a) in C++17, |
− | std::cout << " | + | // std::ssize(a) in C++20 |
+ | std::cout << "Array of length " << length_a << " has elements: "; | ||
for (int i = 0; i < length_a; ++i) | for (int i = 0; i < length_a; ++i) | ||
− | std::cout << a[i] << | + | std::cout << a[i] << ' '; |
+ | std::cout << '\n'; | ||
} | } | ||
|output= | |output= | ||
4! = 24 | 4! = 24 | ||
8! = 40320 | 8! = 40320 | ||
− | + | The number of lowercase letters in "Hello, world!" is 9 | |
− | + | Array of length 12 has elements: 0 1 2 3 4 5 6 7 8 0 0 0 | |
}} | }} | ||
===Defect reports=== | ===Defect reports=== | ||
{{dr list begin}} | {{dr list begin}} | ||
− | {{dr list item|wg=cwg|dr=1712|std=C++14|before=a constexpr variable template was required to have all<br>its declarations contain the {{c|constexpr}} specifier<br>(it is redundant because there cannot be more than one<br>declaration of a variable template with the {{c|constexpr}} specifier)|after=not required anymore}} | + | {{dr list item|wg=cwg|dr=1712|std=C++14|before=a constexpr variable template was required to have all<br>its declarations contain the {{c/core|constexpr}} specifier<br>(it is redundant because there cannot be more than one<br>declaration of a variable template with the {{c/core|constexpr}} specifier)|after=not required anymore}} |
{{dr list item|wg=cwg|dr=1911|std=c++11|before=constexpr constructors for non-literal types were not allowed|after=allowed in constant initialization}} | {{dr list item|wg=cwg|dr=1911|std=c++11|before=constexpr constructors for non-literal types were not allowed|after=allowed in constant initialization}} | ||
{{dr list item|wg=cwg|dr=2004|std=c++11|before=copy/move of a union with a mutable member<br>was allowed in a constant expression|after=mutable variants disqualify<br>implicit copy/move}} | {{dr list item|wg=cwg|dr=2004|std=c++11|before=copy/move of a union with a mutable member<br>was allowed in a constant expression|after=mutable variants disqualify<br>implicit copy/move}} | ||
Line 211: | Line 254: | ||
{{dsc inc|cpp/language/dsc consteval}} | {{dsc inc|cpp/language/dsc consteval}} | ||
{{dsc inc|cpp/language/dsc constinit}} | {{dsc inc|cpp/language/dsc constinit}} | ||
+ | {{dsc see c|c/language/constexpr}} | ||
{{dsc end}} | {{dsc end}} | ||
{{langlinks|de|es|fr|it|ja|pt|ru|zh}} | {{langlinks|de|es|fr|it|ja|pt|ru|zh}} |
Latest revision as of 15:06, 12 August 2024
constexpr
- specifies that the value of a variable or function can appear in constant expressions
Contents |
[edit] Explanation
The constexpr specifier declares that it is possible to evaluate the value of the function or variable at compile time. Such variables and functions can then be used where only compile time constant expressions are allowed (provided that appropriate function arguments are given).
A constexpr specifier used in an object declaration or non-static member function(until C++14) implies const. A constexpr specifier used in a function or static data member(since C++17) declaration implies inline. If any declaration of a function or function template has a constexpr specifier, then every declaration must contain that specifier.
[edit] constexpr variable
A constexpr variable must satisfy the following requirements:
- its type must be a LiteralType
- it must be immediately initialized
- the full-expression of its initialization, including all implicit conversions, constructors calls, etc, must be a constant expression
If a constexpr variable is not translation-unit-local, it should not be initialized to refer to a translation-unit-local entity that is usable in constant expressions, nor have a subobject that refers to such an entity. Such initialization is disallowed in a module interface unit (outside its private-module-fragment, if any) or a module partition, and is deprecated in any other context. |
(since C++20) |
[edit] constexpr function
A constexpr function must satisfy the following requirements:
|
(until C++20) |
|
(since C++20) |
- for constructor and destructor(since C++20), the class must have no virtual base classes
|
(until C++23) |
|
(until C++14) | ||
|
(since C++14) (until C++23) |
constexpr constructorA constexpr constructor whose function body is not = delete; must satisfy the following additional requirements:
constexpr destructor
|
(until C++23) |
For constexpr function templates and constexpr member functions of class templates, at least one specialization must satisfy the abovementioned requirements. Other specializations are still considered as constexpr, even though a call to such a function cannot appear in a constant expression.
If no specialization of the template would satisfy the requirements for a constexpr function when considered as a non-template function, the template is ill-formed, no diagnostic required. |
(until C++23) |
A constexpr function implicitly becomes an immediate function if it is not marked consteval, uses an immediate function in a non-constant manner, and is
|
(since C++20) |
[edit] Notes
Because the constexpr int f(); constexpr bool b1 = noexcept(f()); // false, undefined constexpr function constexpr int f() { return 0; } constexpr bool b2 = noexcept(f()); // true, f() is a constant expression |
(until C++17) |
It is possible to write a constexpr function whose invocation can never satisfy the requirements of a core constant expression: void f(int& i) // not a constexpr function { i = 0; } constexpr void g(int& i) // well-formed since C++23 { f(i); // unconditionally calls f, cannot be a constant expression } |
(since C++23) |
Constexpr constructors are permitted for classes that aren't literal types. For example, the default constructor of std::shared_ptr is constexpr, allowing constant initialization.
Reference variables can be declared constexpr (their initializers have to be reference constant expressions):
static constexpr int const& x = 42; // constexpr reference to a const int object // (the object has static storage duration // due to life extension by a static reference)
Even though try blocks and inline assembly are allowed in constexpr functions, throwing exceptions or executing the assembly is still disallowed in a constant expression. If a variable has constant destruction, there is no need to generate machine code in order to call destructor for it, even if its destructor is not trivial. A non-lambda, non-special-member, and non-templated constexpr function cannot implicitly become an immediate function. Users need to explicitly mark it consteval to make such an intended function definition well-formed. |
(since C++20) |
Feature-test macro | Value | Std | Feature |
---|---|---|---|
__cpp_constexpr |
200704L | (C++11) | constexpr |
201304L | (C++14) | Relaxed constexpr, non-const constexpr methods | |
201603L | (C++17) | Constexpr lambda | |
201907L | (C++20) | Trivial default initialization and asm-declaration in constexpr functions | |
202002L | (C++20) | Changing the active member of a union in constant evaluation | |
202110L | (C++23) | Non-literal variables, labels, and goto statements in constexpr functions | |
202207L | (C++23) | Relaxing some constexpr restrictions | |
202211L | (C++23) | Permitting static constexpr variables in constexpr functions | |
202306L | (C++26) | Constexpr cast from void*: towards constexpr type-erasure | |
__cpp_constexpr_in_decltype |
201711L | (C++11) (DR) |
Generation of function and variable definitions when needed for constant evaluation |
__cpp_constexpr_dynamic_alloc |
201907L | (C++20) | Operations for dynamic storage duration in constexpr functions |
[edit] Keywords
[edit] Example
Defines C++11/14 constexpr functions that compute factorials; defines a literal type that extends string literals:
#include <iostream> #include <stdexcept> // C++11 constexpr functions use recursion rather than iteration constexpr int factorial(int n) { return n <= 1 ? 1 : (n * factorial(n - 1)); } // C++14 constexpr functions may use local variables and loops #if __cplusplus >= 201402L constexpr int factorial_cxx14(int n) { int res = 1; while (n > 1) res *= n--; return res; } #endif // C++14 // A literal class class conststr { const char* p; std::size_t sz; public: template<std::size_t N> constexpr conststr(const char(&a)[N]): p(a), sz(N - 1) {} // constexpr functions signal errors by throwing exceptions // in C++11, they must do so from the conditional operator ?: constexpr char operator[](std::size_t n) const { return n < sz ? p[n] : throw std::out_of_range(""); } constexpr std::size_t size() const { return sz; } }; // C++11 constexpr functions had to put everything in a single return statement // (C++14 does not have that requirement) constexpr std::size_t countlower(conststr s, std::size_t n = 0, std::size_t c = 0) { return n == s.size() ? c : 'a' <= s[n] && s[n] <= 'z' ? countlower(s, n + 1, c + 1) : countlower(s, n + 1, c); } // An output function that requires a compile-time constant, for testing template<int n> struct constN { constN() { std::cout << n << '\n'; } }; int main() { std::cout << "4! = "; constN<factorial(4)> out1; // computed at compile time volatile int k = 8; // disallow optimization using volatile std::cout << k << "! = " << factorial(k) << '\n'; // computed at run time std::cout << "The number of lowercase letters in \"Hello, world!\" is "; constN<countlower("Hello, world!")> out2; // implicitly converted to conststr constexpr int a[12] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; constexpr int length_a = sizeof a / sizeof(int); // std::size(a) in C++17, // std::ssize(a) in C++20 std::cout << "Array of length " << length_a << " has elements: "; for (int i = 0; i < length_a; ++i) std::cout << a[i] << ' '; std::cout << '\n'; }
Output:
4! = 24 8! = 40320 The number of lowercase letters in "Hello, world!" is 9 Array of length 12 has elements: 0 1 2 3 4 5 6 7 8 0 0 0
[edit] Defect reports
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
DR | Applied to | Behavior as published | Correct behavior |
---|---|---|---|
CWG 1712 | C++14 | a constexpr variable template was required to have all its declarations contain the constexpr specifier (it is redundant because there cannot be more than one declaration of a variable template with the constexpr specifier) |
not required anymore |
CWG 1911 | C++11 | constexpr constructors for non-literal types were not allowed | allowed in constant initialization |
CWG 2004 | C++11 | copy/move of a union with a mutable member was allowed in a constant expression |
mutable variants disqualify implicit copy/move |
CWG 2163 | C++14 | labels were allowed in constexpr functions even though gotos are prohibited |
labels also prohibited |
CWG 2268 | C++11 | copy/move of a union with a mutable member was prohibited by the resolution of CWG issue 2004 |
allowed if the object is created within the constant expression |
[edit] See also
constant expression | defines an expression that can be evaluated at compile time |
consteval specifier(C++20)
|
specifies that a function is an immediate function, that is, every call to the function must be in a constant evaluation |
constinit specifier(C++20)
|
asserts that a variable has static initialization, i.e. zero initialization and constant initialization |
C documentation for constexpr
|