Namespaces
Variants
Views
Actions

Difference between revisions of "cpp/language/constexpr"

From cppreference.com
< cpp‎ | language
(Simplify)
(Merge {{rrev}} blocks)
Line 18: Line 18:
 
::* it is not of class type nor (possibly multi-dimensional) array thereof, or
 
::* it is not of class type nor (possibly multi-dimensional) array thereof, or
 
::* 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}}.
}}
 
  
{{rrev|since=c++20|
 
 
If a {{tt|constexpr}} variable is not {{rlp|tu local|translation-unit-local}}, it should not be initialized to point to, or refer to, or have a (possibly recursive) subobject that points to or refers to, a translation-unit-local entity that is usable in constant expressions. 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.
 
If a {{tt|constexpr}} variable is not {{rlp|tu local|translation-unit-local}}, it should not be initialized to point to, or refer to, or have a (possibly recursive) subobject that points to or refers to, a translation-unit-local entity that is usable in constant expressions. 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.
 
}}
 
}}

Revision as of 22:06, 28 September 2022

 
 
C++ language
General topics
Flow control
Conditional execution statements
if
Iteration statements (loops)
for
range-for (C++11)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications (until C++17*)
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
explicit (C++11)
static

Special member functions
Templates
Miscellaneous
 
 

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:

  • it must have constant destruction, i.e. either:
  • it is not of class type nor (possibly multi-dimensional) array thereof, or
  • it is of class type or (possibly multi-dimensional) array thereof, that class type has a constexpr destructor, and for a hypothetical expression e whose only effect is to destroy the object, 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 e.

If a constexpr variable is not translation-unit-local, it should not be initialized to point to, or refer to, or have a (possibly recursive) subobject that points to or refers to, a translation-unit-local entity that is usable in constant expressions. 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)

A constexpr function must satisfy the following requirements:

(until C++20)

|rev2=

  • it must not be a coroutine}}
  • its return value (if any) and each of its parameters must be of 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.
  • the function body must be either deleted or defaulted or contain only the following:
(until C++14)
  • the function body must not contain:
  • a goto statement
  • a statement with a label other than case and default
(until C++20)
  • a definition of a variable of non-literal type
  • a definition of a variable of static or thread storage duration
(A function body that is =default; or =delete; contains none of the above.)
(since C++14)
(until C++23)

A constexpr constructor whose function body is not =delete; must satisfy the following additional requirements:

  • for the constructor of a class or struct, every base class sub-object and every non-variant non-static data member must be initialized. If the class is a union-like class, for each of its non-empty anonymous union members, exactly one variant member must be initialized
  • for the constructor of a non-empty union, exactly one non-static data member must be initialized
(until C++20)
  • every constructor selected to initializing non-static data members and base class must be a constexpr constructor.

Destructors cannot be constexpr, but a trivial destructor can be implicitly called in constant expressions.

(until C++20)

A constexpr destructor whose function body is not =delete; must satisfy the following additional requirements:

  • every destructor used to destroy non-static data members and base class must be a constexpr destructor.
(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 true for a constant expression, it can be used to check if a particular invocation of a constexpr function takes the constant expression branch:

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)
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
__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

Keywords

constexpr

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
 
    constexpr int a[12] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
    constexpr int length_a = sizeof(a)/sizeof(int);
    std::cout << "array of length " << length_a << " has elements: ";
    for (int i = 0; i < length_a; ++i)
        std::cout << a[i] << " ";
}

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

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 CWG issue 2004
allowed if the object is 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[edit]
constinit specifier(C++20) asserts that a variable has static initialization, i.e. zero initialization and constant initialization[edit]