Difference between revisions of "cpp/language/constexpr"
(Making the size const as well) |
(Undo revision 139469 by 2605:A601:A739:9300:8761:2F2D:382:6E1E (talk). const data members are bad) |
||
Line 122: | Line 122: | ||
class conststr { | class conststr { | ||
const char* p; | const char* p; | ||
− | + | std::size_t sz; | |
public: | public: | ||
template<std::size_t N> | template<std::size_t N> |
Revision as of 02:52, 30 April 2022
constexpr
- specifies that the value of a variable or function can appear in constant expressions
Contents |
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.
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
|
(since C++20) |
If a |
(since C++20) |
A constexpr
function must satisfy the following requirements:
|
(until C++20) |
|
(since C++20) |
- its return type (if any) must be a LiteralType
- each of its parameters must be a LiteralType
- for constructor and destructor(since C++20), the class must have no virtual base classes
- there exists at least one set of argument values such that an invocation of the function could be an evaluated subexpression of a core constant expression (for constructors, use in a constant initializer is sufficient)(since C++14). No diagnostic is required for a violation of this bullet.
|
(until C++20) |
|
(until C++14) | ||
|
(since C++14) (until C++23) |
A constexpr
constructor whose function body is not =delete; must satisfy the following additional requirements:
|
(until C++20) |
- every constructor selected to initializing non-static data members and base class must be a constexpr constructor.
Destructors cannot be |
(until C++20) |
A
|
(since C++20) |
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.
Notes
Because the noexcept operator always returns 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) |
Constexpr constructors are permitted for classes that aren't literal types. For example, the default constructor of std::unique_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. |
(since C++20) |
Keywords
Example
Definition of a C++11 constexpr function which computes factorials and a literal type that extends string literals:
#include <iostream> #include <stdexcept> // C++11 constexpr functions use recursion rather than iteration // (C++14 constexpr functions may use local variables and loops) constexpr int factorial(int n) { return n <= 1 ? 1 : (n * factorial(n - 1)); } // 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 doesn't 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); } // 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 }
Output:
4! = 24 8! = 40320 the number of lowercase letters in "Hello, world!" is 9
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 1911 | C++11 | constexpr constructors for
non-literal types were not allowed |
allowed in constant initialization |
CWG 2004 | C++14 | 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++14 | copy/move of a union with a mutable member
was prohibited by CWG 2004 |
allowed if the object was created
within the constant expression |
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 |