Constant expressions

From cppreference.com
< cpp‎ | language
 
 
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function declaration
inline specifier
Exception specifications (until C++20)
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
decltype (C++11)
auto (C++11)
alignas (C++11)
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
Implicit conversions - Explicit conversions
static_cast - dynamic_cast
const_cast - reinterpret_cast
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous
 
 

Defines an expression that can be evaluated at compile time.

Such expressions can be used as non-type template arguments, array sizes, and in other contexts that require constant expressions, e.g.

int n = 1;
std::array<int, n> a1; // error: n is not a constant expression
const int cn = 2;
std::array<int, cn> a2; // OK: cn is a constant expression

Core constant expressions

A core constant expression is any expression whose evaluation would not evaluate any one of the following:

  1. the this pointer, except in a constexpr function or a constexpr constructor that is being evaluated as part of the expression
  2. a function call expression that calls a function (or a constructor) that is not declared constexpr
    constexpr int n = std::numeric_limits<int>::max(); // OK: max() is constexpr
    constexpr int m = std::time(nullptr); // Error: std::time() is not constexpr
  3. a function call to a constexpr function which is declared, but not defined
  4. a function call to a constexpr function/constructor template instantiation where the instantiation fails to satisfy constexpr function/constructor requirements.
  5. (since C++20) a function call to a constexpr virtual function, invoked on an object not usable in constant expressions (see below) and whose lifetime began outside this expression.
  6. an expression that would exceed the implementation-defined limits
  7. an expression whose evaluation leads to any form of core language undefined behavior (including signed integer overflow, division by zero, pointer arithmetic outside array bounds, etc). Whether standard library undefined behavior is detected is unspecified.
    constexpr double d1 = 2.0/1.0; // OK
    constexpr double d2 = 2.0/0.0; // Error: not defined
    constexpr int n = std::numeric_limits<int>::max() + 1; // Error: overflow
    int x, y, z[30];
    constexpr auto e1 = &y - &x; // Error: undefined
    constexpr auto e2 = &z[20] - &z[3]; // OK
    constexpr std::bitset<2> a; 
    constexpr bool b = a[2]; // UB, but unspecified if detected
  8. (until C++17) a lambda expression
  9. an lvalue-to-rvalue implicit conversion unless....
    1. applied to a non-volatile glvalue that designates an object that is usable in constant expressions (see below),
      int main() {
          const std::size_t tabsize = 50;
          int tab[tabsize]; // OK: tabsize is a constant expression
                            // because tabsize is usable in constant expressions
                            // because it has const-qualified integral type, and
                            // its initializer is a constant initializer
       
          std::size_t n = 50;
          const std::size_t sz = n;
          int tab2[sz]; // error: sz is not a constant expression
                        // because sz is not usable in constant expressions
                        // because its initializer was not a constant initializer
      }
    2. or applied to a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of this expression
  10. an lvalue-to-rvalue implicit conversion or modification applied to a non-active member of a union or its subobject (even if it shares a common initial sequence with the active member)
  11. (since C++20) an lvalue-to-rvalue implicit conversion applied to an object with an indeterminate value
  12. an invocation of an implicitly-defined copy/move constructor or copy/move assignment operator for a union whose active member (if any) is mutable, unless the lifetime of the union object began within the evaluation of this expression
  13. (since C++17) (until C++20) an assignment expression or invocation of an overloaded assignment operator that would change the active member of a union
  14. an id-expression referring to a variable or a data member of reference type, unless the reference is usable in constant expressions (see below) or its lifetime began within the evaluation of this expression
  15. conversion from cv void* to any pointer-to-object type
  16. (until C++20)dynamic_cast
  17. reinterpret_cast
  18. (until C++20) pseudo-destructor call
  19. (until C++14) an increment or a decrement operator
  20. (since C++14) modification of an object, unless the object has non-volatile literal type and its lifetime began within the evaluation of the expression

    constexpr int incr(int& n) {
      return ++n;
    }
    constexpr int g(int k) {
      constexpr int x = incr(k); // error: incr(k) is not a core constant
                                 // expression because lifetime of k
                                 // began outside the expression incr(k)
      return x;
    }
    constexpr int h(int k) {
      int x = incr(k); // OK: x is not required to be initialized with a core
                       // constant expression
      return x;
    }
    constexpr int y = h(1); // OK: initializes y with the value 2
                            // h(1) is a core constant expression because
                            // the lifetime of k begins inside the expression h(1)
  21. (until C++20) a typeid expression applied to a glvalue of polymorphic type
  22. a new-expression, unless the selected allocation function is a replaceable global allocation function and the allocated storage is deallocated within the evaluation of this expression (since C++20)
  23. a delete-expression, unless it deallocates a region of storage allocated within the evaluation of this expession (since C++20)
  24. (since C++20) a call to std::allocator<T>::allocate, unless the allocated storage is deallocated within the evaluation of this expression
  25. (since C++20) a call to std::allocator<T>::deallocate, unless it deallocates a region of storage allocated within the evaluation of this expession
  26. (since C++20) an await-expression or a yield-expression
  27. (since C++20) a three-way comparison when the result is unspecified
  28. an equality or relational operator when the result is unspecified
  29. (until C++14) an assignment or a compound assignment operator
  30. a throw expression
  31. (since C++20) an asm-declaration
  32. (since C++14) an invocation of the va_arg macro, whether an invocation of the va_start macro can be evaluated is unspecified
  33. (since C++20) a dynamic_cast or typeid expression that would throw an exception
  34. inside a lambda-expression, a reference to this or to a variable defined outside that lambda, if that reference would be an odr-use
    void g() {
      const int n=0;
      constexpr int j=*&n; // OK: outside of a lambda-expression
      [=]{ constexpr int i=n;  // OK: 'n' is not odr-used and not captured here.
           constexpr int j=*&n;// Ill-formed: '&n' would be an odr-use of 'n'.
         };
    }

    note that if the ODR-use takes place in a function call to a closure, it does not refer to this or to an enclosing variable, since it accesses a closure's data member instead

    // OK: 'v' & 'm' are odr-used but do not occur in a constant-expression
    // within the nested lambda
    auto monad = [](auto v){return [=]{return v;};};
    auto bind = [](auto m){return [=](auto fvm){return fvm(m());};};
    // OK to have captures to automatic objects created during constant expression evaluation.
    static_assert(bind(monad(2))(monad)() == monad(2)());
    (since C++17)

Note: Just being a core constant expression does not have any direct semantic meaning: an expression has to be one of the following subsets to be used in certain contexts:

Usable in constant expressions

In the list above, a variable is usable in constant expressions at a point P if

  • the variable is
  • of reference type or
  • of const-qualified integral or enumeration type
  • and the definition of the variable is reachable from P
  • and, if P is not in the same translation unit as the definition of the variable (that is, the definition is imported), the variable is not 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
(since C++20)

An object or reference is usable in constant expressions if it is

  • a variable that is usable in constant expressions, or
  • (since C++20) a template parameter object, or
  • a string literal object, or
  • a non-mutable subobject or reference member of any of the above, or
  • a complete temporary object of non-volatile const-qualified integral or enumeration type that is initialized with a constant expression.
const std::size sz = 10; // sz is usable in constant expressions

Integral constant expression

Integral constant expression is an expression of integral or unscoped enumeration type implicitly converted to a prvalue, where the converted expression is a core constant expression. If an expression of class type is used where an integral constant expression is expected, the expression is contextually implicitly converted to an integral or unscoped enumeration type.

The following contexts require an integral constant expression:

(until C++14)
  • null-pointer constants
(until C++14)

Converted constant expression

A converted constant expression of type T is an expression implicitly converted to type T, where the converted expression is a constant expression, and the implicit conversion sequence contains only:

  • constexpr user-defined conversions (so a class can be used where integral type is expected)
  • lvalue-to-rvalue conversions
  • integral promotions
  • non-narrowing integral conversions
  • array-to-pointer conversions
  • function-to-pointer conversions
  • function pointer conversions (pointer to noexcept function to pointer to function)
  • qualification conversions
  • null pointer conversions from std::nullptr_t
  • null member pointer conversions from std::nullptr_t
(since C++17)
  • And if any reference binding takes place, it is direct binding (not one that constructs a temporary object)

The following contexts require a converted constant expression:

(since C++14)

A contextually converted constant expression of type bool is an expression, contextually converted to bool, where the converted expression is a constant expression and the conversion sequence contains only the conversions above.

The following contexts require a contextually converted constant expression of type bool:

(since C++17)
(since C++20)

Constant expression

A constant expression is either

  • a glvalue core constant expression that refers to
  • an object with static storage duration that is not a temporary, or
  • an object with static storage duration that is a temporary, but whose value satisfies the constraints for prvalues below, or
  • a non-immediate (since C++20) function
  • a prvalue core constant expression whose value satisfies the following constraints:
  • if the value is an object of class type, each non-static data member of reference type refers to an entity that satisfies the constraints for glvalues above
  • if the value is of pointer type, it holds
  • address of an object with static storage duration
  • address past the end of an object with static storage duration
  • address of a non-immediate (since C++20) function
  • a null pointer value
  • if the value is of pointer-to-member-function type, it does not designate an immediate function (since C++20)
  • if the value is an object of class or array type, each subobject satisfies these constraints for values
(since C++14)

A constant expression is a literal constant expression, a reference constant expression, or an address constant expression.

Literal constant expression is a prvalue core constant expression of non-pointer literal type (after conversions as required by context). A literal constant expression of array or class type requires that each subobject is initialized with a constant expression.

Reference constant expression is an lvalue core constant expression that designates an object with static storage duration or a function.

Address constant expression is a prvalue core constant expression (after conversions required by context) of type std::nullptr_t or of a pointer type, which points to an object with static storage duration, one past the end of an array with static storage duration, to a function, or is a null pointer.

(until C++14)
void test() {
    static const int a = std::random_device{}();
    constexpr const int& ra = a; // OK: a is a glvalue constant expression
    constexpr int ia = a; // Error: a is not a prvalue constant expression
 
    const int b = 42;
    constexpr const int& rb = b; // Error: b is not a glvalue constant expression
    constexpr int ib = b; // OK: b is a prvalue constant expression
}

Notes

Implementations are not permitted to declare library functions as constexpr unless the standard says the function is constexpr

Named return value optimization (NRVO) is not permitted in constant expressions, while return value optimization (RVO) is mandatory.

(since C++14)

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 1313 C++11 undefined behavior was permitted, and all pointer subtraction was prohibited same-array pointer subtraction ok, UB prohibited
CWG 1952 C++11 standard library undefined behavior was required to be diagnosed unspecified whether library UB is diagnosed
CWG 2167 C++11 non-member references local to an evaluation were making the evaluation non-constexpr non-member references allowed
CWG 2299 C++14 whether macros in <cstdarg> can be used in constant evaluation was not clear va_arg forbidden, va_start unspecified

See also

constexpr specifier(C++11) specifies that the value of a variable or function can be computed at compile time
(C++11)(deprecated in C++17)(removed in C++20)
checks if a type is a literal type
(class template)