[expr.const] (original) (raw)
7 Expressions [expr]
7.7 Constant expressions [expr.const]
Certain contexts require expressions that satisfy additional requirements as detailed in this subclause; other contexts have different semantics depending on whether or not an expression satisfies these requirements.
Expressions that satisfy these requirements, assuming that copy elision is not performed, are calledconstant expressions.
[Note 1:
Constant expressions can be evaluated during translation.
— _end note_]
A variable or temporary object o is constant-initialized if
- either it has an initializer or its default-initialization results in some initialization being performed, and
- the full-expression of its initialization is a constant expression when interpreted as a constant-expression, except that if o is an object, that full-expression may also invoke constexpr constructors for o and its subobjects even if those objects are of non-literal class types.
[Note 2:
Such a class can have a non-trivial destructor.
Within this evaluation,std::is_constant_evaluated() ([meta.const.eval]) returns true.
— _end note_]
A variable is potentially-constant if it is constexpr or it has reference or const-qualified integral or enumeration type.
A constant-initialized potentially-constant variable V isusable in constant expressions at a point P ifV's initializing declaration D is reachable from P and
- V is constexpr,
- V is not initialized to a TU-local value, or
- P is in the same translation unit as D.
An object or reference is usable in constant expressions if it is
- a variable that is usable in constant expressions, or
- a template parameter object, or
- a string literal object, or
- a temporary object of non-volatile const-qualified literal type whose lifetime is extended ([class.temporary]) to that of a variable that is usable in constant expressions, or
- a non-mutable subobject or reference member of any of the above.
An expression E is a core constant expressionunless the evaluation of E, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following:
- this, except in aconstexpr function that is being evaluated as part of E;
- an invocation of a non-constexpr function83;
- an invocation of an undefined constexpr function;
- an invocation of an instantiated constexpr function that fails to satisfy the requirements for a constexpr function;
- an invocation of a virtual functionfor an object unless
- the object is usable in constant expressions or
- its lifetime began within the evaluation of E;
- an expression that would exceed the implementation-defined limits (see [implimits]);
- an operation that would have undefined behavior as specified in [intro] through [cpp]84;
- an lvalue-to-rvalue conversion unless it is applied to
- a non-volatile glvalue that refers to an object that is usable in constant expressions, or
- a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of E;
- an lvalue-to-rvalue conversion that is applied to a glvalue that refers to a non-active member of a union or a subobject thereof;
- an lvalue-to-rvalue conversion that is applied to an object with an indeterminate value;
- 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 E;
- an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either
- it is usable in constant expressions or
- its lifetime began within the evaluation of E;
- in a lambda-expression, a reference to this or to a variable with automatic storage duration defined outside thatlambda-expression, where the reference would be an odr-use;
[Example 1: void g() { const int n = 0;[=] { constexpr int i = n; constexpr int j = *&n; };} — end example_]
[_Note 3:
If the odr-use occurs in an invocation of a function call operator of a closure type, it no longer refers to this or to an enclosing automatic variable due to the transformation ([expr.prim.lambda.capture]) of the id-expression into an access of the corresponding data member.
[Example 2: auto monad = [](auto v) { return [=] { return v; }; };auto bind = [](auto m) { return [=](auto fvm) { return fvm(m()); };};static_assert(bind(monad(2))(monad)() == monad(2)()); — _end example_]
— _end note_] - a conversion from type cv void* to a pointer-to-object type;
- a reinterpret_cast ([expr.reinterpret.cast]);
- a modification of an object ([expr.ass], [expr.post.incr],[expr.pre.incr]) unless it is applied to a non-volatile lvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of E;
- a new-expression ([expr.new]), unless the selected allocation function is a replaceable global allocation function ([new.delete.single],[new.delete.array]) and the allocated storage is deallocated within the evaluation of E;
- a delete-expression ([expr.delete]), unless it deallocates a region of storage allocated within the evaluation of E;
- a call to an instance ofstd::allocator<T>::allocate ([allocator.members]), unless the allocated storage is deallocated within the evaluation of E;
- a call to an instance ofstd::allocator<T>::deallocate ([allocator.members]), unless it deallocates a region of storage allocated within the evaluation of E;
- an await-expression ([expr.await]);
- a yield-expression ([expr.yield]);
- a three-way comparison ([expr.spaceship]), relational ([expr.rel]), or equality ([expr.eq]) operator where the result is unspecified;
- a throw-expression ([expr.throw]);
- a dynamic_cast ([expr.dynamic.cast]) or typeid ([expr.typeid]) expression that would throw an exception;
- an asm-declaration ([dcl.asm]); or
- an invocation of the va_arg macro ([cstdarg.syn]).
If E satisfies the constraints of a core constant expression, but evaluation of E would evaluate an operation that has undefined behavior as specified in [library] through [thread], or an invocation of the va_start macro ([cstdarg.syn]), it is unspecified whether E is a core constant expression.
[Example 3: int x; struct A { constexpr A(bool b) : m(b?42:x) { } int m;};constexpr int v = A(true).m; constexpr int w = A(false).m; constexpr int f1(int k) { constexpr int x = k; return x;} constexpr int f2(int k) { int x = k; return x;} constexpr int incr(int &n) { return ++n;} constexpr int g(int k) { constexpr int x = incr(k); return x;} constexpr int h(int k) { int x = incr(k); return x;} constexpr int y = h(1); — _end example_]
For the purposes of determining whether an expression E is a core constant expression, the evaluation of a call to a member function of std::allocator<T>as defined in [allocator.members], where T is a literal type, does not disqualify E from being a core constant expression, even if the actual evaluation of such a call would otherwise fail the requirements for a core constant expression.
Similarly, the evaluation of a call tostd::destroy_at,std::ranges::destroy_at,std::construct_at, orstd::ranges::construct_atdoes not disqualify Efrom being a core constant expression unless:
- for a call to std::construct_at or std::ranges::construct_at, the first argument, of type T*, does not point to storage allocated with std::allocator<T> or to an object whose lifetime began within the evaluation of E, or the evaluation of the underlying constructor call disqualifies E from being a core constant expression, or
- for a call to std::destroy_at or std::ranges::destroy_at, the first argument, of type T*, does not point to storage allocated with std::allocator<T> or to an object whose lifetime began within the evaluation of E, or the evaluation of the underlying destructor call disqualifies E from being a core constant expression.
An object a is said to have constant destruction if:
- 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 a,E would be a core constant expression if the lifetime of a and its non-mutable subobjects (but not its mutable subobjects) were considered to start within E.
An integral constant expressionis an expression of integral or unscoped enumeration type, implicitly converted to a prvalue, where the converted expression is a core constant expression.
[Note 4:
Such expressions can be used as bit-field lengths ([class.bit]), as enumerator initializers if the underlying type is not fixed ([dcl.enum]), and as alignments.
— _end note_]
If an expression of literal class type is used in a context where an integral constant expression is required, then that expression is contextually implicitly converted ([conv]) to an integral or unscoped enumeration type and the selected conversion function shall be constexpr.
[Example 4: struct A { constexpr A(int i) : val(i) { } constexpr operator int() const { return val; } constexpr operator long() const { return 42; } private: int val;};constexpr A a = alignof(int);alignas(a) int n; struct B { int n : a; }; — _end example_]
A converted constant expressionof 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
- user-defined conversions,
- lvalue-to-rvalue conversions,
- array-to-pointer conversions,
- function-to-pointer conversions,
- qualification conversions,
- integral promotions,
- integral conversions other thannarrowing conversions,
- null pointer conversions from std::nullptr_t,
- null member pointer conversions from std::nullptr_t, and
- function pointer conversions,
and where the reference binding (if any) binds directly.
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.
A constant expression is either a glvalue core constant expression that refers to an entity that is a permitted result of a constant expression (as defined below), or 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 is a permitted result of a constant expression,
- if the value is of pointer type, it contains the address of an object with static storage duration, the address past the end of such an object ([expr.add]), the address of a non-immediate function, or a null pointer value,
- if the value is of pointer-to-member-function type, it does not designate an immediate function, and
- if the value is an object of class or array type, each subobject satisfies these constraints for the value.
An entity is apermitted result of a constant expressionif it is an object with static storage duration that either is not a temporary object or is a temporary object whose value satisfies the above constraints, or if it is a non-immediate function.
[Example 5: consteval int f() { return 42; } consteval auto g() { return f; } consteval int h(int (*p)() = g()) { return p(); } constexpr int r = h(); constexpr auto e = g(); — _end example_]
Recommended practice: Implementations should provide consistent results of floating-point evaluations, irrespective of whether the evaluation is performed during translation or during program execution.
[Note 6:
Since this document imposes no restrictions on the accuracy of floating-point operations, it is unspecified whether the evaluation of a floating-point expression during translation yields the same result as the evaluation of the same expression (or the same operations on the same values) during program execution.
[Example 6: bool f() { char array[1 + int(1 + 0.2 - 0.1 - 0.1)]; int size = 1 + int(1 + 0.2 - 0.1 - 0.1); return sizeof(array) == size;}
It is unspecified whether the value of f() will be true or false.
— _end example_]
— _end note_]
An expression or conversion is in an immediate function contextif it is potentially evaluated and its innermost non-block scope is a function parameter scope of an immediate function.
An expression or conversion is an immediate invocationif it is a potentially-evaluated explicit or implicit invocation of an immediate function and is not in an immediate function context.
An immediate invocation shall be a constant expression.
An expression or conversion is manifestly constant-evaluatedif it is:
- a constant-expression, or
- the condition of a constexpr if statement ([stmt.if]), or
- an immediate invocation, or
- the result of substitution into an atomic constraint expression to determine whether it is satisfied ([temp.constr.atomic]), or
- the initializer of a variable that is usable in constant expressions or has constant initialization ([basic.start.static]).85
[Example 7: template<bool> struct X {}; Xstd::is\_constant\_evaluated()\ x; int y;const int a = std::is_constant_evaluated() ? y : 1; double z[a]; const int b = std::is_constant_evaluated() ? 2 : y; int c = y + (std::is_constant_evaluated() ? 2 : y); constexpr int f() { const int n = std::is_constant_evaluated() ? 13 : 17; int m = std::is_constant_evaluated() ? 13 : 17; char arr[n] = {}; return m + sizeof(arr);} int p = f(); int q = p + f(); — _end example_]
[Note 7:
A manifestly constant-evaluated expression is evaluated even in an unevaluated operand.
— _end note_]
An expression or conversion is potentially constant evaluatedif it is:
- a manifestly constant-evaluated expression,
- a potentially-evaluated expression,
- an immediate subexpression of a braced-init-list,86
- an expression of the form & cast-expressionthat occurs within a templated entity,87or
- a subexpression of one of the above that is not a subexpression of a nested unevaluated operand.
A function or variable isneeded for constant evaluationif it is:
- a constexpr function thatis named byan expression that is potentially constant evaluated, or
- a variable whose name appears as a potentially constant evaluated expression that is either a constexpr variable or is of non-volatile const-qualified integral type or of reference type.