1121 lines
53 KiB
Markdown
1121 lines
53 KiB
Markdown
[expr.const]
|
||
|
||
# 7 Expressions [[expr]](./#expr)
|
||
|
||
## 7.7 Constant expressions [expr.const]
|
||
|
||
[1](#1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L8242)
|
||
|
||
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[.](#1.sentence-1)
|
||
|
||
Expressions that satisfy these requirements,
|
||
assuming that [copy elision](class.copy.elision "11.9.6 Copy/move elision [class.copy.elision]") is not performed,
|
||
are called[*constant expressions*](#def:constant_expression "7.7 Constant expressions [expr.const]")[.](#1.sentence-2)
|
||
|
||
[*Note [1](#note-1)*:
|
||
|
||
Constant expressions can be evaluated
|
||
during translation[.](#1.sentence-3)
|
||
|
||
â *end note*]
|
||
|
||
[constant-expression:](#nt:constant-expression "7.7 Constant expressions [expr.const]")
|
||
[*conditional-expression*](expr.cond#nt:conditional-expression "7.6.16 Conditional operator [expr.cond]")
|
||
|
||
[2](#2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L8261)
|
||
|
||
The [*constituent values*](#def:constituent_value "7.7 Constant expressions [expr.const]") of an object o are
|
||
|
||
- [(2.1)](#2.1)
|
||
|
||
if o has scalar type, the value of o;
|
||
|
||
- [(2.2)](#2.2)
|
||
|
||
otherwise, the constituent values of any direct subobjects of o other than inactive union members[.](#2.sentence-1)
|
||
|
||
The [*constituent references*](#def:constituent_reference "7.7 Constant expressions [expr.const]") of an object o are
|
||
|
||
- [(2.3)](#2.3)
|
||
|
||
any direct members of o that have reference type, and
|
||
|
||
- [(2.4)](#2.4)
|
||
|
||
the constituent references of any direct subobjects of o other than inactive union members[.](#2.sentence-2)
|
||
|
||
[3](#3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L8279)
|
||
|
||
The constituent values and constituent references of
|
||
a variable x are defined as follows:
|
||
|
||
- [(3.1)](#3.1)
|
||
|
||
If x declares an object,
|
||
the constituent values and references of that object are
|
||
constituent values and references of x[.](#3.1.sentence-1)
|
||
|
||
- [(3.2)](#3.2)
|
||
|
||
If x declares a reference,
|
||
that reference is a constituent reference of x[.](#3.2.sentence-1)
|
||
|
||
For any constituent reference r of a variable x,
|
||
if r is bound to a temporary object or subobject thereof
|
||
whose lifetime is extended to that of r,
|
||
the constituent values and references of that temporary object
|
||
are also constituent values and references of x, recursively[.](#3.sentence-2)
|
||
|
||
[4](#4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L8297)
|
||
|
||
An object o is [*constexpr-referenceable*](#def:constexpr-referenceable "7.7 Constant expressions [expr.const]") from a point P if
|
||
|
||
- [(4.1)](#4.1)
|
||
|
||
o has static storage duration, or
|
||
|
||
- [(4.2)](#4.2)
|
||
|
||
o has automatic storage duration, and, letting v denote
|
||
* [(4.2.1)](#4.2.1)
|
||
|
||
the variable corresponding to o's complete object or
|
||
|
||
* [(4.2.2)](#4.2.2)
|
||
|
||
the variable to whose lifetime that of o is extended,
|
||
|
||
the smallest scope enclosing v and the smallest scope enclosing P that are neither
|
||
* [(4.2.3)](#4.2.3)
|
||
|
||
block scopes nor
|
||
|
||
* [(4.2.4)](#4.2.4)
|
||
|
||
function parameter scopes associated with
|
||
a [*requirement-parameter-list*](expr.prim.req.general#nt:requirement-parameter-list "7.5.8.1 General [expr.prim.req.general]")
|
||
|
||
are the same function parameter scope.
|
||
|
||
[*Example [1](#example-1)*: struct A {int m; const int& r;};void f() {static int sx; thread_local int tx; // tx is never constexpr-referenceableint ax;
|
||
A aa = {1, 2}; static A sa = {3, 4}; // The objects sx, ax, and aa.m, sa.m, and the temporaries to which aa.r and sa.r are bound, are constexpr-referenceable.auto lambda = [] {int ay; // The objects sx, sa.m, and ay (but not ax or aa), and the// temporary to which sa.r is bound, are constexpr-referenceable.};} â *end example*]
|
||
|
||
[5](#5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L8343)
|
||
|
||
An object or reference x is[*constexpr-representable*](#def:constexpr-representable "7.7 Constant expressions [expr.const]") at a point P if,
|
||
for each constituent value of x that points to or past an object o,
|
||
and for each constituent reference of x that refers to an object o,o is constexpr-referenceable from P[.](#5.sentence-1)
|
||
|
||
[6](#6)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L8350)
|
||
|
||
A variable v is [*constant-initializable*](#def:constant-initializable "7.7 Constant expressions [expr.const]") if
|
||
|
||
- [(6.1)](#6.1)
|
||
|
||
the full-expression of its initialization is a constant expression
|
||
when interpreted as a [*constant-expression*](#nt:constant-expression "7.7 Constant expressions [expr.const]") with all contract assertions
|
||
using the ignore evaluation semantic ([[basic.contract.eval]](basic.contract.eval "6.11.2 Evaluation")),
|
||
[*Note [2](#note-2)*:
|
||
Within this evaluation,std::is_constant_evaluated() ([[meta.const.eval]](meta.const.eval "21.3.12 Constant evaluation context"))
|
||
returns true[.](#6.1.sentence-1)
|
||
â *end note*]
|
||
[*Note [3](#note-3)*:
|
||
The initialization, when evaluated,
|
||
can still evaluate contract assertions
|
||
with other evaluation semantics,
|
||
resulting in a diagnostic or ill-formed program
|
||
if a contract violation occurs[.](#6.1.sentence-2)
|
||
â *end note*]
|
||
|
||
- [(6.2)](#6.2)
|
||
|
||
immediately after the initializing declaration of v,
|
||
the object or reference x declared by v is constexpr-representable, and
|
||
|
||
- [(6.3)](#6.3)
|
||
|
||
if x has static or thread storage duration,x is constexpr-representable at the nearest point
|
||
whose immediate scope is a namespace scope
|
||
that follows the initializing declaration of v[.](#6.sentence-1)
|
||
|
||
[7](#7)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L8382)
|
||
|
||
A constant-initializable variable is [*constant-initialized*](#def:constant-initialized "7.7 Constant expressions [expr.const]") if either it has an initializer or
|
||
its type is const-default-constructible ([[dcl.init.general]](dcl.init.general "9.5.1 General"))[.](#7.sentence-1)
|
||
|
||
[*Example [2](#example-2)*: void f() {int ax = 0; // ax is constant-initializedthread_local int tx = 0; // tx is constant-initializedstatic int sx; // sx is not constant-initializedstatic int& rss = sx; // rss is constant-initializedstatic int& rst = tx; // rst is not constant-initializedstatic int& rsa = ax; // rsa is not constant-initializedthread_local int& rts = sx; // rts is constant-initializedthread_local int& rtt = tx; // rtt is not constant-initializedthread_local int& rta = ax; // rta is not constant-initializedint& ras = sx; // ras is constant-initializedint& rat = tx; // rat is not constant-initializedint& raa = ax; // raa is constant-initialized} â *end example*]
|
||
|
||
[8](#8)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L8405)
|
||
|
||
A variable is [*potentially-constant*](#def:potentially-constant "7.7 Constant expressions [expr.const]") if
|
||
it is constexpr or
|
||
it has reference or non-volatile const-qualified integral or enumeration type[.](#8.sentence-1)
|
||
|
||
[9](#9)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L8410)
|
||
|
||
A constant-initialized potentially-constant variable V is[*usable in constant expressions*](#def:usable_in_constant_expressions "7.7 Constant expressions [expr.const]") at a point P ifV's initializing declaration D is reachable from P and
|
||
|
||
- [(9.1)](#9.1)
|
||
|
||
V is constexpr,
|
||
|
||
- [(9.2)](#9.2)
|
||
|
||
V is not initialized to a TU-local value, or
|
||
|
||
- [(9.3)](#9.3)
|
||
|
||
P is in the same translation unit as D[.](#9.sentence-1)
|
||
|
||
An object or reference is[*potentially usable in constant expressions*](#def:potentially_usable_in_constant_expressions "7.7 Constant expressions [expr.const]") at point P if it is
|
||
|
||
- [(9.4)](#9.4)
|
||
|
||
the object or reference declared by a variable
|
||
that is usable in constant expressions at P,
|
||
|
||
- [(9.5)](#9.5)
|
||
|
||
a temporary object of non-volatile const-qualified literal type
|
||
whose lifetime is extended ([[class.temporary]](class.temporary "6.8.7 Temporary objects"))
|
||
to that of a variable that is usable in constant expressions at P,
|
||
|
||
- [(9.6)](#9.6)
|
||
|
||
a [template parameter](temp.param "13.2 Template parameters [temp.param]") object,
|
||
|
||
- [(9.7)](#9.7)
|
||
|
||
a [string literal](lex.string "5.13.5 String literals [lex.string]") object,
|
||
|
||
- [(9.8)](#9.8)
|
||
|
||
a non-mutable subobject of any of the above, or
|
||
|
||
- [(9.9)](#9.9)
|
||
|
||
a reference member of any of the above[.](#9.sentence-2)
|
||
|
||
An object or reference is [*usable in constant expressions*](#def:usable_in_constant_expressions "7.7 Constant expressions [expr.const]") at point P if it is an object or reference
|
||
that is potentially usable in constant expressions at P and
|
||
is constexpr-representable at P[.](#9.sentence-3)
|
||
|
||
[*Example [3](#example-3)*: struct A {int* const & r;};void f(int x) {constexpr A a = {&x}; static_assert(a.r == &x); // OK[&] {static_assert(a.r != nullptr); // error: a.r is not usable in constant expressions at this point}();} â *end example*]
|
||
|
||
[10](#10)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L8457)
|
||
|
||
An expression E is a [*core constant expression*](#def:expression,core_constant "7.7 Constant expressions [expr.const]") unless the evaluation of E, following the rules of the abstract
|
||
machine ([[intro.execution]](intro.execution "6.10.1 Sequential execution")), would evaluate one of the following:
|
||
|
||
- [(10.1)](#10.1)
|
||
|
||
this ([[expr.prim.this]](expr.prim.this "7.5.3 This")), except
|
||
* [(10.1.1)](#10.1.1)
|
||
|
||
in a constexpr function ([[dcl.constexpr]](dcl.constexpr "9.2.6 The constexpr and consteval specifiers"))
|
||
that is being evaluated as part of E or
|
||
|
||
* [(10.1.2)](#10.1.2)
|
||
|
||
when appearing as the [*postfix-expression*](expr.post.general#nt:postfix-expression "7.6.1.1 General [expr.post.general]") of
|
||
an implicit or explicit class member access expression ([[expr.ref]](expr.ref "7.6.1.5 Class member access"));
|
||
|
||
- [(10.2)](#10.2)
|
||
|
||
a control flow that passes through
|
||
a declaration of a block variable ([[basic.scope.block]](basic.scope.block "6.4.3 Block scope")) with
|
||
static ([[basic.stc.static]](basic.stc.static "6.8.6.2 Static storage duration")) or
|
||
thread ([[basic.stc.thread]](basic.stc.thread "6.8.6.3 Thread storage duration")) storage duration,
|
||
unless that variable is usable in constant expressions;
|
||
[*Example [4](#example-4)*: constexpr char test() {static const int x = 5; static constexpr char c[] = "Hello World"; return *(c + x);}static_assert(' ' == test()); â *end example*]
|
||
|
||
- [(10.3)](#10.3)
|
||
|
||
an invocation of a non-constexpr function;[67](#footnote-67 "Overload resolution ([over.match]) is applied as usual.")
|
||
|
||
- [(10.4)](#10.4)
|
||
|
||
an invocation of an undefined constexpr function;
|
||
|
||
- [(10.5)](#10.5)
|
||
|
||
an invocation of an instantiated constexpr function
|
||
that is not constexpr-suitable;
|
||
|
||
- [(10.6)](#10.6)
|
||
|
||
an invocation of a virtual function ([[class.virtual]](class.virtual "11.7.3 Virtual functions"))
|
||
for an object whose dynamic type is constexpr-unknown;
|
||
|
||
- [(10.7)](#10.7)
|
||
|
||
an expression that would exceed the implementation-defined
|
||
limits (see [[implimits]](implimits "Annex B (informative) Implementation quantities"));
|
||
|
||
- [(10.8)](#10.8)
|
||
|
||
an operation that would have undefined or erroneous behavior
|
||
as specified in [[intro]](intro "4 General principles") through [[cpp]](cpp "15 Preprocessing directives");[68](#footnote-68 "This includes, for example, signed integer overflow ([expr.pre]), certain pointer arithmetic ([expr.add]), division by zero ([expr.mul]), or certain shift operations ([expr.shift]).")
|
||
|
||
- [(10.9)](#10.9)
|
||
|
||
an [lvalue-to-rvalue conversion](conv.lval "7.3.2 Lvalue-to-rvalue conversion [conv.lval]") unless
|
||
it is applied to
|
||
* [(10.9.1)](#10.9.1)
|
||
|
||
a glvalue of type cv std::nullptr_t,
|
||
|
||
* [(10.9.2)](#10.9.2)
|
||
|
||
a non-volatile glvalue that refers to an object that is
|
||
usable in constant expressions, or
|
||
|
||
* [(10.9.3)](#10.9.3)
|
||
|
||
a non-volatile glvalue of literal type that refers to a non-volatile object
|
||
whose lifetime began within the evaluation of E;
|
||
|
||
- [(10.10)](#10.10)
|
||
|
||
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;
|
||
|
||
- [(10.11)](#10.11)
|
||
|
||
an lvalue-to-rvalue conversion that is applied to
|
||
an object with an [indeterminate value](basic.indet#def:value,indeterminate "6.8.5 Indeterminate and erroneous values [basic.indet]");
|
||
|
||
- [(10.12)](#10.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 E;
|
||
|
||
- [(10.13)](#10.13)
|
||
|
||
in a [*lambda-expression*](expr.prim.lambda.general#nt:lambda-expression "7.5.6.1 General [expr.prim.lambda.general]"),
|
||
a reference to this or to a variable with
|
||
automatic storage duration defined outside that[*lambda-expression*](expr.prim.lambda.general#nt:lambda-expression "7.5.6.1 General [expr.prim.lambda.general]"), where
|
||
the reference would be an odr-use ([[basic.def.odr]](basic.def.odr#term.odr.use "6.3 One-definition rule"), [[expr.prim.lambda]](expr.prim.lambda "7.5.6 Lambda expressions"));
|
||
[*Example [5](#example-5)*: void g() {const int n = 0; [=] {constexpr int i = n; // OK, n is not odr-used hereconstexpr int j = *&n; // error: &n would be an odr-use of n};} â *end example*]
|
||
[*Note [4](#note-4)*:
|
||
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
|
||
variable with automatic storage duration
|
||
due to the transformation ([[expr.prim.lambda.capture]](expr.prim.lambda.capture "7.5.6.3 Captures"))
|
||
of the [*id-expression*](expr.prim.id.general#nt:id-expression "7.5.5.1 General [expr.prim.id.general]") into
|
||
an access of the corresponding data member[.](#10.13.sentence-1)
|
||
[*Example [6](#example-6)*: auto monad = [](auto v) { return [=] { return v; }; };auto bind = [](auto m) {return [=](auto fvm) { return fvm(m()); };};
|
||
|
||
// OK to capture objects with automatic storage duration created during constant expression evaluation.static_assert(bind(monad(2))(monad)() == monad(2)()); â *end example*]
|
||
â *end note*]
|
||
|
||
- [(10.14)](#10.14)
|
||
|
||
a conversion
|
||
from a prvalue P of type âpointer to cv voidâ
|
||
to a type â*cv1* pointer to Tâ,
|
||
where T is not *cv2* void,
|
||
unless P is a null pointer value or
|
||
points to an object whose type is similar to T;
|
||
|
||
- [(10.15)](#10.15)
|
||
|
||
a reinterpret_cast ([[expr.reinterpret.cast]](expr.reinterpret.cast "7.6.1.10 Reinterpret cast"));
|
||
|
||
- [(10.16)](#10.16)
|
||
|
||
a modification of an object ([[expr.assign]](expr.assign "7.6.19 Assignment and compound assignment operators"), [[expr.post.incr]](expr.post.incr "7.6.1.6 Increment and decrement"), [[expr.pre.incr]](expr.pre.incr "7.6.2.3 Increment and decrement"))
|
||
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;
|
||
|
||
- [(10.17)](#10.17)
|
||
|
||
an invocation of a destructor ([[class.dtor]](class.dtor "11.4.7 Destructors")) or a function call
|
||
whose [*postfix-expression*](expr.post.general#nt:postfix-expression "7.6.1.1 General [expr.post.general]") names a pseudo-destructor ([[expr.call]](expr.call "7.6.1.3 Function call")),
|
||
in either case for an object whose lifetime did not begin within the evaluation of E;
|
||
|
||
- [(10.18)](#10.18)
|
||
|
||
a [*new-expression*](expr.new#nt:new-expression "7.6.2.8 New [expr.new]") ([[expr.new]](expr.new "7.6.2.8 New")),
|
||
unless either
|
||
* [(10.18.1)](#10.18.1)
|
||
|
||
the selected allocation function is
|
||
a replaceable global allocation function ([[new.delete.single]](new.delete.single "17.6.3.2 Single-object forms"), [[new.delete.array]](new.delete.array "17.6.3.3 Array forms")) and
|
||
the allocated storage is deallocated within the evaluation of E, or
|
||
|
||
* [(10.18.2)](#10.18.2)
|
||
|
||
the selected allocation function is
|
||
a non-allocating form ([[new.delete.placement]](new.delete.placement "17.6.3.4 Non-allocating forms"))
|
||
with an allocated type T, where
|
||
+
|
||
[(10.18.2.1)](#10.18.2.1)
|
||
the placement argument to the [*new-expression*](expr.new#nt:new-expression "7.6.2.8 New [expr.new]") points to
|
||
an object whose type is similar to T ([[conv.qual]](conv.qual "7.3.6 Qualification conversions")) or,
|
||
if T is an array type,
|
||
to the first element of an object of a type similar to T, and
|
||
|
||
+
|
||
[(10.18.2.2)](#10.18.2.2)
|
||
the placement argument points to storage
|
||
whose duration began within the evaluation of E;
|
||
|
||
- [(10.19)](#10.19)
|
||
|
||
a [*delete-expression*](expr.delete#nt:delete-expression "7.6.2.9 Delete [expr.delete]") ([[expr.delete]](expr.delete "7.6.2.9 Delete")),
|
||
unless it deallocates a region of storage
|
||
allocated within the evaluation of E;
|
||
|
||
- [(10.20)](#10.20)
|
||
|
||
a call to an instance ofstd::allocator<T>::allocate ([[allocator.members]](allocator.members "20.2.10.2 Members")),
|
||
unless the allocated storage is deallocated within the evaluation of E;
|
||
|
||
- [(10.21)](#10.21)
|
||
|
||
a call to an instance ofstd::allocator<T>::deallocate ([[allocator.members]](allocator.members "20.2.10.2 Members")),
|
||
unless it deallocates a region of storage
|
||
allocated within the evaluation of E;
|
||
|
||
- [(10.22)](#10.22)
|
||
|
||
a construction of an exception object,
|
||
unless the exception object and
|
||
all of its implicit copies created by invocations ofstd::current_exception or std::rethrow_exception ([[propagation]](propagation "17.9.7 Exception propagation"))
|
||
are destroyed within the evaluation of E;
|
||
|
||
- [(10.23)](#10.23)
|
||
|
||
an [*await-expression*](expr.await#nt:await-expression "7.6.2.4 Await [expr.await]") ([[expr.await]](expr.await "7.6.2.4 Await"));
|
||
|
||
- [(10.24)](#10.24)
|
||
|
||
a [*yield-expression*](expr.yield#nt:yield-expression "7.6.17 Yielding a value [expr.yield]") ([[expr.yield]](expr.yield "7.6.17 Yielding a value"));
|
||
|
||
- [(10.25)](#10.25)
|
||
|
||
a three-way comparison ([[expr.spaceship]](expr.spaceship "7.6.8 Three-way comparison operator")),
|
||
relational ([[expr.rel]](expr.rel "7.6.9 Relational operators")), or equality ([[expr.eq]](expr.eq "7.6.10 Equality operators"))
|
||
operator where the result is unspecified;
|
||
|
||
- [(10.26)](#10.26)
|
||
|
||
a dynamic_cast ([[expr.dynamic.cast]](expr.dynamic.cast "7.6.1.7 Dynamic cast")) ortypeid ([[expr.typeid]](expr.typeid "7.6.1.8 Type identification")) expression
|
||
on a glvalue that refers to an object
|
||
whose dynamic type is constexpr-unknown;
|
||
|
||
- [(10.27)](#10.27)
|
||
|
||
a dynamic_cast ([[expr.dynamic.cast]](expr.dynamic.cast "7.6.1.7 Dynamic cast")) expression,typeid ([[expr.typeid]](expr.typeid "7.6.1.8 Type identification")) expression, ornew-expression ([[expr.new]](expr.new "7.6.2.8 New"))
|
||
that would throw an exception
|
||
where no definition of the exception type is reachable;
|
||
|
||
- [(10.28)](#10.28)
|
||
|
||
an expression that would produce an injected declaration (see below),
|
||
unless E is the corresponding expression of
|
||
a [*consteval-block-declaration*](dcl.pre#nt:consteval-block-declaration "9.1 Preamble [dcl.pre]") ([[dcl.pre]](dcl.pre "9.1 Preamble"));
|
||
|
||
- [(10.29)](#10.29)
|
||
|
||
an [*asm-declaration*](dcl.asm#nt:asm-declaration "9.11 The asm declaration [dcl.asm]") ([[dcl.asm]](dcl.asm "9.11 The asm declaration"));
|
||
|
||
- [(10.30)](#10.30)
|
||
|
||
an invocation of the va_arg macro ([[cstdarg.syn]](cstdarg.syn "17.14.2 Header <cstdarg> synopsis"));
|
||
|
||
- [(10.31)](#10.31)
|
||
|
||
a non-constant library call ([[defns.nonconst.libcall]](defns.nonconst.libcall "3.35 non-constant library call"));
|
||
or
|
||
|
||
- [(10.32)](#10.32)
|
||
|
||
a goto statement ([[stmt.goto]](stmt.goto "8.8.6 The goto statement"))[.](#10.sentence-1)
|
||
[*Note [5](#note-5)*:
|
||
A goto statement introduced by equivalence ([[stmt]](stmt "8 Statements"))
|
||
is not in scope[.](#10.32.sentence-2)
|
||
For example, a while statement ([[stmt.while]](stmt.while "8.6.2 The while statement"))
|
||
can be executed during constant evaluation[.](#10.32.sentence-3)
|
||
â *end note*]
|
||
|
||
[11](#11)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L8710)
|
||
|
||
It isimplementation-defined
|
||
whether E is a core constant expression
|
||
if E satisfies the constraints of a core constant expression, but
|
||
evaluation of E has runtime-undefined behavior[.](#11.sentence-1)
|
||
|
||
[12](#12)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L8717)
|
||
|
||
It is unspecified whether E is a core constant expression
|
||
if E satisfies the constraints of a core constant expression, but
|
||
evaluation of E would evaluate
|
||
|
||
- [(12.1)](#12.1)
|
||
|
||
an operation that has undefined behavior
|
||
as specified in [[library]](library "16 Library introduction") through [[exec]](exec "33 Execution control library") or
|
||
|
||
- [(12.2)](#12.2)
|
||
|
||
an invocation of the va_start macro ([[cstdarg.syn]](cstdarg.syn "17.14.2 Header <cstdarg> synopsis"))[.](#12.sentence-1)
|
||
|
||
[13](#13)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L8729)
|
||
|
||
[*Example [7](#example-7)*: int x; // not constantstruct A {constexpr A(bool b) : m(b?42:x) { }int m;};constexpr int v = A(true).m; // OK, constructor call initializes m with the value 42constexpr int w = A(false).m; // error: initializer for m is x, which is non-constantconstexpr int f1(int k) {constexpr int x = k; // error: x is not initialized by a constant expression// because lifetime of k began outside the initializer of xreturn x;}constexpr int f2(int k) {int x = k; // OK, not required to be a constant expression// because x is not constexprreturn x;}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, incr(k) is not required to be a core constant expressionreturn 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 h(1) â *end example*]
|
||
|
||
[14](#14)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L8770)
|
||
|
||
For the purposes of determining
|
||
whether an expression E is a core constant expression,
|
||
the evaluation of the body of a member function of std::allocator<T> as defined in [[allocator.members]](allocator.members "20.2.10.2 Members"), where T is a literal type,
|
||
is ignored[.](#14.sentence-1)
|
||
|
||
[15](#15)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L8777)
|
||
|
||
For the purposes of determining whether E is a core constant expression,
|
||
the evaluation of a call to
|
||
a trivial copy/move constructor or copy/move assignment operator of a union
|
||
is considered to copy/move the active member of the union, if any[.](#15.sentence-1)
|
||
|
||
[*Note [6](#note-6)*:
|
||
|
||
The copy/move of the active member is trivial[.](#15.sentence-2)
|
||
|
||
â *end note*]
|
||
|
||
[16](#16)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L8786)
|
||
|
||
For the purposes of determining whether E is a core constant expression,
|
||
the evaluation of an [*id-expression*](expr.prim.id.general#nt:id-expression "7.5.5.1 General [expr.prim.id.general]") that names a structured binding v ([[dcl.struct.bind]](dcl.struct.bind "9.7 Structured binding declarations")) has the
|
||
following semantics:
|
||
|
||
- [(16.1)](#16.1)
|
||
|
||
If v is an lvalue referring to the object bound to an invented reference r,
|
||
the behavior is as if r were nominated[.](#16.1.sentence-1)
|
||
|
||
- [(16.2)](#16.2)
|
||
|
||
Otherwise, if v names an array element or class member,
|
||
the behavior is that of
|
||
evaluating e[i] or e.m, respectively,
|
||
where e is the name of the variable
|
||
initialized from the initializer of the structured binding declaration, andi is the index of the element referred to by v orm is the name of the member referred to by v, respectively[.](#16.2.sentence-1)
|
||
|
||
[*Example [8](#example-8)*: #include <tuple>struct S {mutable int m; constexpr S(int m): m(m) {}virtual int g() const;};void f(std::tuple<S&> t) {auto [r] = t; static_assert(r.g() >= 0); // error: dynamic type is constexpr-unknownconstexpr auto [m] = S(1); static_assert(m == 1); // error: lvalue-to-rvalue conversion on mutable// subobject e.m, where e is a constexpr object of type Susing A = int[2]; constexpr auto [v0, v1] = A{2, 3}; static_assert(v0 + v1 == 5); // OK, equivalent to e[0] + e[1] where e is a constexpr array} â *end example*]
|
||
|
||
[17](#17)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L8825)
|
||
|
||
During the evaluation of an expression E as a core constant expression,
|
||
all [*id-expression*](expr.prim.id.general#nt:id-expression "7.5.5.1 General [expr.prim.id.general]")*s*, [*splice-expression*](expr.prim.splice#nt:splice-expression "7.5.9 Expression splicing [expr.prim.splice]")*s*, and
|
||
uses of *this that refer to an object or reference
|
||
whose lifetime did not begin with the evaluation of E are treated as referring to a specific instance of that object or reference
|
||
whose lifetime and that of all subobjects (including all union members)
|
||
includes the entire constant evaluation[.](#17.sentence-1)
|
||
|
||
For such an object that is not usable in constant expressions,
|
||
the dynamic type of the object is [*constexpr-unknown*](#def:constexpr-unknown "7.7 Constant expressions [expr.const]")[.](#17.sentence-2)
|
||
|
||
For such a reference that is not usable in constant expressions,
|
||
the reference is treated as binding to
|
||
an unspecified object of the referenced type
|
||
whose lifetime and that of all subobjects includes
|
||
the entire constant evaluation and whose dynamic type is constexpr-unknown[.](#17.sentence-3)
|
||
|
||
[*Example [9](#example-9)*: template <typename T, size_t N>constexpr size_t array_size(T (&)[N]) {return N;}void use_array(int const (&gold_medal_mel)[2]) {constexpr auto gold = array_size(gold_medal_mel); // OK}constexpr auto olympic_mile() {const int ledecky = 1500; return []{ return ledecky; };}static_assert(olympic_mile()() == 1500); // OKstruct Swim {constexpr int phelps() { return 28; }virtual constexpr int lochte() { return 12; }int coughlin = 12;};
|
||
|
||
constexpr int how_many(Swim& swam) { Swim* p = &swam; return (p + 1 - 1)->phelps();}void splash(Swim& swam) {static_assert(swam.phelps() == 28); // OKstatic_assert((&swam)->phelps() == 28); // OK Swim* pswam = &swam; static_assert(pswam->phelps() == 28); // error: lvalue-to-rvalue conversion on a pointer// not usable in constant expressionsstatic_assert(how_many(swam) == 28); // OKstatic_assert(Swim().lochte() == 12); // OKstatic_assert(swam.lochte() == 12); // error: invoking virtual function on reference// with constexpr-unknown dynamic typestatic_assert(swam.coughlin == 12); // error: lvalue-to-rvalue conversion on an object// not usable in constant expressions}extern Swim dc;extern Swim& trident;
|
||
|
||
constexpr auto& sandeno = typeid(dc); // OK, can only be typeid(Swim)constexpr auto& gallagher = typeid(trident); // error: constexpr-unknown dynamic type â *end example*]
|
||
|
||
[18](#18)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L8895)
|
||
|
||
An object a is said to have [*constant destruction*](#def:destruction,constant "7.7 Constant expressions [expr.const]") if
|
||
|
||
- [(18.1)](#18.1)
|
||
|
||
it is not of class type nor (possibly multidimensional) array thereof, or
|
||
|
||
- [(18.2)](#18.2)
|
||
|
||
it is of class type or (possibly multidimensional) array thereof,
|
||
that class type has a constexpr destructor ([[dcl.constexpr]](dcl.constexpr "9.2.6 The constexpr and consteval specifiers")), 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[.](#18.sentence-1)
|
||
|
||
[19](#19)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L8910)
|
||
|
||
An [*integral constant expression*](#def:expression,integral_constant "7.7 Constant expressions [expr.const]") is an expression of integral or
|
||
unscoped enumeration type, implicitly converted to a prvalue, where the converted expression is a core constant expression[.](#19.sentence-1)
|
||
|
||
[*Note [7](#note-7)*:
|
||
|
||
Such expressions can be
|
||
used as bit-field lengths ([[class.bit]](class.bit "11.4.10 Bit-fields")), as enumerator
|
||
initializers if the underlying type is not fixed ([[dcl.enum]](dcl.enum "9.8.1 Enumeration declarations")),
|
||
and as [alignments](dcl.align "9.13.2 Alignment specifier [dcl.align]")[.](#19.sentence-2)
|
||
|
||
â *end note*]
|
||
|
||
[20](#20)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L8921)
|
||
|
||
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]](conv "7.3 Standard conversions")) to an integral or unscoped
|
||
enumeration type
|
||
and the selected conversion function shall be constexpr[.](#20.sentence-1)
|
||
|
||
[*Example [10](#example-10)*: 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; // error: ambiguous conversionstruct B { int n : a; }; // error: ambiguous conversion â *end example*]
|
||
|
||
[21](#21)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L8942)
|
||
|
||
A [*converted constant expression*](#def:expression,converted_constant "7.7 Constant expressions [expr.const]") 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
|
||
|
||
- [(21.1)](#21.1)
|
||
|
||
user-defined conversions,
|
||
|
||
- [(21.2)](#21.2)
|
||
|
||
lvalue-to-rvalue conversions ([[conv.lval]](conv.lval "7.3.2 Lvalue-to-rvalue conversion")),
|
||
|
||
- [(21.3)](#21.3)
|
||
|
||
array-to-pointer conversions ([[conv.array]](conv.array "7.3.3 Array-to-pointer conversion")),
|
||
|
||
- [(21.4)](#21.4)
|
||
|
||
function-to-pointer conversions ([[conv.func]](conv.func "7.3.4 Function-to-pointer conversion")),
|
||
|
||
- [(21.5)](#21.5)
|
||
|
||
qualification conversions ([[conv.qual]](conv.qual "7.3.6 Qualification conversions")),
|
||
|
||
- [(21.6)](#21.6)
|
||
|
||
integral promotions ([[conv.prom]](conv.prom "7.3.7 Integral promotions")),
|
||
|
||
- [(21.7)](#21.7)
|
||
|
||
integral conversions ([[conv.integral]](conv.integral "7.3.9 Integral conversions")) other than narrowing conversions ([[dcl.init.list]](dcl.init.list "9.5.5 List-initialization")),
|
||
|
||
- [(21.8)](#21.8)
|
||
|
||
floating-point promotions ([[conv.fpprom]](conv.fpprom "7.3.8 Floating-point promotion")),
|
||
|
||
- [(21.9)](#21.9)
|
||
|
||
floating-point conversions ([[conv.double]](conv.double "7.3.10 Floating-point conversions")) where
|
||
the source value can be represented exactly in the destination type,
|
||
|
||
- [(21.10)](#21.10)
|
||
|
||
null pointer conversions ([[conv.ptr]](conv.ptr "7.3.12 Pointer conversions")) from std::nullptr_t,
|
||
|
||
- [(21.11)](#21.11)
|
||
|
||
null member pointer conversions ([[conv.mem]](conv.mem "7.3.13 Pointer-to-member conversions")) from std::nullptr_t, and
|
||
|
||
- [(21.12)](#21.12)
|
||
|
||
function pointer conversions ([[conv.fctptr]](conv.fctptr "7.3.14 Function pointer conversions")),
|
||
|
||
and where the reference binding (if any) binds directly[.](#21.sentence-1)
|
||
|
||
[*Note [8](#note-8)*:
|
||
|
||
Such expressions can be used in new expressions ([[expr.new]](expr.new "7.6.2.8 New")), as case expressions ([[stmt.switch]](stmt.switch "8.5.3 The switch statement")),
|
||
as enumerator initializers if the underlying type is
|
||
fixed ([[dcl.enum]](dcl.enum "9.8.1 Enumeration declarations")), as array bounds ([[dcl.array]](dcl.array "9.3.4.5 Arrays")),
|
||
as constant template arguments ([[temp.arg]](temp.arg "13.4 Template arguments")),
|
||
and as the constant expression of a [*splice-specifier*](basic.splice#nt:splice-specifier "6.6 Splice specifiers [basic.splice]") ([[basic.splice]](basic.splice "6.6 Splice specifiers"))[.](#21.sentence-2)
|
||
|
||
â *end note*]
|
||
|
||
A [*contextually converted constant expression of type bool*](#def:contextually_converted_constant_expression_of_type_bool) is
|
||
an expression, contextually converted to bool ([[conv]](conv "7.3 Standard conversions")),
|
||
where the converted expression is a constant expression and
|
||
the conversion sequence contains only the conversions above[.](#21.sentence-3)
|
||
|
||
[22](#22)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L8979)
|
||
|
||
A [*constant expression*](#def:expression,constant "7.7 Constant expressions [expr.const]") is either
|
||
|
||
- [(22.1)](#22.1)
|
||
|
||
a glvalue core constant expression E for which
|
||
* [(22.1.1)](#22.1.1)
|
||
|
||
E refers to a non-immediate function,
|
||
|
||
* [(22.1.2)](#22.1.2)
|
||
|
||
E designates an object o, and
|
||
if the complete object of o is of consteval-only type then so is E,
|
||
[*Example [11](#example-11)*: struct Base { };struct Derived : Base { std::meta::info r; };
|
||
|
||
consteval const Base& fn(const Derived& derived) { return derived; }constexpr Derived obj{.r=^^::}; // OKconstexpr const Derived& d = obj; // OKconstexpr const Base& b = fn(obj); // error: not a constant expression because Derived// is a consteval-only type but Base is not. â *end example*]
|
||
|
||
or
|
||
|
||
- [(22.2)](#22.2)
|
||
|
||
a prvalue core constant expression whose result object ([[basic.lval]](basic.lval "7.2.1 Value category"))
|
||
satisfies the following constraints:
|
||
* [(22.2.1)](#22.2.1)
|
||
|
||
each constituent reference refers to an object or a non-immediate function,
|
||
|
||
* [(22.2.2)](#22.2.2)
|
||
|
||
no constituent value of scalar type is an indeterminate or erroneous value ([[basic.indet]](basic.indet "6.8.5 Indeterminate and erroneous values")),
|
||
|
||
* [(22.2.3)](#22.2.3)
|
||
|
||
no constituent value of pointer type is a pointer to an immediate function or
|
||
an invalid pointer value ([[basic.compound]](basic.compound "6.9.4 Compound types")),
|
||
|
||
* [(22.2.4)](#22.2.4)
|
||
|
||
no constituent value of pointer-to-member type designates an immediate function, and
|
||
|
||
* [(22.2.5)](#22.2.5)
|
||
|
||
unless the value is of consteval-only type,
|
||
+
|
||
[(22.2.5.1)](#22.2.5.1)
|
||
no constituent value of pointer-to-member type points to
|
||
a direct member of a consteval-only class type,
|
||
|
||
+
|
||
[(22.2.5.2)](#22.2.5.2)
|
||
no constituent value of pointer type points to or past an object
|
||
whose complete object is of consteval-only type, and
|
||
|
||
+
|
||
[(22.2.5.3)](#22.2.5.3)
|
||
no constituent reference refers to an object
|
||
whose complete object is of consteval-only type[.](#22.sentence-1)
|
||
|
||
[*Note [9](#note-9)*:
|
||
|
||
A glvalue core constant expression
|
||
that either refers to or points to an unspecified object
|
||
is not a constant expression[.](#22.sentence-2)
|
||
|
||
â *end note*]
|
||
|
||
[*Example [12](#example-12)*: consteval int f() { return 42; }consteval auto g() { return f; }consteval int h(int (*p)() = g()) { return p(); }constexpr int r = h(); // OKconstexpr auto e = g(); // error: a pointer to an immediate function is// not a permitted result of a constant expressionstruct S {int x; constexpr S() {}};int i() {constexpr S s; // error: s.x has erroneous value} â *end example*]
|
||
|
||
[23](#23)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L9057)
|
||
|
||
*Recommended practice*: Implementations should provide consistent results of floating-point evaluations,
|
||
irrespective of whether the evaluation is performed
|
||
during translation or during program execution[.](#23.sentence-1)
|
||
|
||
[*Note [10](#note-10)*:
|
||
|
||
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[.](#23.sentence-2)
|
||
|
||
[*Example [13](#example-13)*: bool f() {char array[1 + int(1 + 0.2 - 0.1 - 0.1)]; // Must be evaluated during translationint size = 1 + int(1 + 0.2 - 0.1 - 0.1); // May be evaluated at runtimereturn sizeof(array) == size;}
|
||
|
||
It is unspecified whether the value of f() will be true or false[.](#23.sentence-3)
|
||
|
||
â *end example*]
|
||
|
||
â *end note*]
|
||
|
||
[24](#24)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L9080)
|
||
|
||
An expression or conversion is in an [*immediate function context*](#def:immediate_function_context "7.7 Constant expressions [expr.const]") if it is potentially evaluated and either:
|
||
|
||
- [(24.1)](#24.1)
|
||
|
||
its innermost enclosing non-block scope is
|
||
a function parameter scope of an immediate function,
|
||
|
||
- [(24.2)](#24.2)
|
||
|
||
it is a subexpression of a manifestly constant-evaluated expression
|
||
or conversion, or
|
||
|
||
- [(24.3)](#24.3)
|
||
|
||
its enclosing statement is enclosed ([[stmt.pre]](stmt.pre "8.1 Preamble")) by
|
||
the [*compound-statement*](stmt.block#nt:compound-statement "8.4 Compound statement or block [stmt.block]") of a consteval if statement ([[stmt.if]](stmt.if "8.5.2 The if statement"))[.](#24.sentence-1)
|
||
|
||
An invocation is an [*immediate invocation*](#def:immediate_invocation "7.7 Constant expressions [expr.const]") if it is a potentially-evaluated explicit or implicit invocation of
|
||
an immediate function and
|
||
is not in an immediate function context[.](#24.sentence-2)
|
||
|
||
An aggregate initialization is an immediate invocation
|
||
if it evaluates a default member initializer
|
||
that has a subexpression that is an immediate-escalating expression[.](#24.sentence-3)
|
||
|
||
[25](#25)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L9102)
|
||
|
||
A potentially-evaluated expression or conversion is [*immediate-escalating*](#def:immediate-escalating "7.7 Constant expressions [expr.const]") if it is neither initially in an immediate function context
|
||
nor a subexpression of an immediate invocation, and
|
||
|
||
- [(25.1)](#25.1)
|
||
|
||
it is an [*id-expression*](expr.prim.id.general#nt:id-expression "7.5.5.1 General [expr.prim.id.general]") or [*splice-expression*](expr.prim.splice#nt:splice-expression "7.5.9 Expression splicing [expr.prim.splice]") that designates an immediate function,
|
||
|
||
- [(25.2)](#25.2)
|
||
|
||
it is an immediate invocation that is not a constant expression, or
|
||
|
||
- [(25.3)](#25.3)
|
||
|
||
it is of consteval-only type ([[basic.types.general]](basic.types.general "6.9.1 General"))[.](#25.sentence-1)
|
||
|
||
[26](#26)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L9120)
|
||
|
||
An [*immediate-escalating*](#def:function,immediate-escalating "7.7 Constant expressions [expr.const]") function is
|
||
|
||
- [(26.1)](#26.1)
|
||
|
||
the call operator of a lambda that is not declared
|
||
with the consteval specifier,
|
||
|
||
- [(26.2)](#26.2)
|
||
|
||
a defaulted special member function
|
||
that is not declared with the consteval specifier, or
|
||
|
||
- [(26.3)](#26.3)
|
||
|
||
a function that results from the instantiation
|
||
of a templated entity defined with the constexpr specifier[.](#26.sentence-1)
|
||
|
||
An immediate-escalating expression shall appear only
|
||
in an immediate-escalating function[.](#26.sentence-2)
|
||
|
||
[27](#27)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L9137)
|
||
|
||
An [*immediate function*](#def:function,immediate "7.7 Constant expressions [expr.const]") is a function or constructor that is either
|
||
|
||
- [(27.1)](#27.1)
|
||
|
||
declared with the consteval specifier, or
|
||
|
||
- [(27.2)](#27.2)
|
||
|
||
an immediate-escalating function *F* whose function body contains either
|
||
* [(27.2.1)](#27.2.1)
|
||
|
||
an immediate-escalating expression or
|
||
|
||
* [(27.2.2)](#27.2.2)
|
||
|
||
a definition of a non-constexpr variable with consteval-only type
|
||
|
||
whose innermost enclosing non-block scope
|
||
is *F*'s function parameter scope. [*Note [11](#note-11)*:
|
||
Default member initializers used to initialize
|
||
a base or member subobject ([[class.base.init]](class.base.init "11.9.3 Initializing bases and members"))
|
||
are considered to be part of the function body ([[dcl.fct.def.general]](dcl.fct.def.general "9.6.1 General"))[.](#27.2.sentence-2)
|
||
â *end note*]
|
||
|
||
[*Example [14](#example-14)*: consteval int id(int i) { return i; }constexpr char id(char c) { return c; }template<class T>constexpr int f(T t) {return t + id(t);}auto a = &f<char>; // OK, f<char> is not an immediate functionauto b = &f<int>; // error: f<int> is an immediate functionstatic_assert(f(3) == 6); // OKtemplate<class T>constexpr int g(T t) { // g<int> is not an immediate functionreturn t + id(42); // because id(42) is already a constant}template<class T, class F>constexpr bool is_not(T t, F f) {return not f(t);}consteval bool is_even(int i) { return i % 2 == 0; }static_assert(is_not(5, is_even)); // OKint x = 0;
|
||
|
||
template<class T>constexpr T h(T t = id(x)) { // h<int> is not an immediate function// id(x) is not evaluated when parsing the default argument ([[dcl.fct.default]](dcl.fct.default "9.3.4.7 Default arguments"), [[temp.inst]](temp.inst "13.9.2 Implicit instantiation"))return t;}template<class T>constexpr T hh() { // hh<int> is an immediate function because of the invocationreturn h<T>(); // of the immediate function id in the default argument of h<int>}int i = hh<int>(); // error: hh<int>() is an immediate-escalating expression// outside of an immediate-escalating functionstruct A {int x; int y = id(x);};
|
||
|
||
template<class T>constexpr int k(int) { // k<int> is not an immediate function because A(42) is areturn A(42).y; // constant expression and thus not immediate-escalating}constexpr int l(int c) pre(c >= 2) {return (c % 2 == 0) ? c / 0 : c;}const int i0 = l(0); // dynamic initialization; contract violation or undefined behaviorconst int i1 = l(1); // static initialization; value of 1 or contract violation at compile timeconst int i2 = l(2); // dynamic initialization; undefined behaviorconst int i3 = l(3); // static initialization; value of 3 â *end example*]
|
||
|
||
[28](#28)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L9223)
|
||
|
||
An expression or conversion is [*manifestly constant-evaluated*](#def:manifestly_constant-evaluated "7.7 Constant expressions [expr.const]") if it is:
|
||
|
||
- [(28.1)](#28.1)
|
||
|
||
a [*constant-expression*](#nt:constant-expression "7.7 Constant expressions [expr.const]"), or
|
||
|
||
- [(28.2)](#28.2)
|
||
|
||
the condition of a constexpr if statement ([[stmt.if]](stmt.if "8.5.2 The if statement")), or
|
||
|
||
- [(28.3)](#28.3)
|
||
|
||
an immediate invocation, or
|
||
|
||
- [(28.4)](#28.4)
|
||
|
||
the result of substitution into an atomic constraint expression
|
||
to determine whether it is satisfied ([[temp.constr.atomic]](temp.constr.atomic "13.5.2.3 Atomic constraints")), or
|
||
|
||
- [(28.5)](#28.5)
|
||
|
||
the initializer of a variable
|
||
that is usable in constant expressions or
|
||
has constant initialization ([[basic.start.static]](basic.start.static "6.10.3.2 Static initialization"))[.](#28.sentence-1)[69](#footnote-69 "Testing this condition can involve a trial evaluation of its initializer, with evaluations of contract assertions using the ignore evaluation semantic ([basic.contract.eval]), as described above.")
|
||
[*Example [15](#example-15)*: template<bool> struct X {};
|
||
X<std::is_constant_evaluated()> x; // type X<true>int y;const int a = std::is_constant_evaluated() ? y : 1; // dynamic initialization to 1double z[a]; // error: a is not usable// in constant expressionsconst int b = std::is_constant_evaluated() ? 2 : y; // static initialization to 2int c = y + (std::is_constant_evaluated() ? 2 : y); // dynamic initialization to y+yconstexpr int f() {const int n = std::is_constant_evaluated() ? 13 : 17; // n is 13int m = std::is_constant_evaluated() ? 13 : 17; // m can be 13 or 17 (see below)char arr[n] = {}; // char[13]return m + sizeof(arr);}int p = f(); // m is 13; initialized to 26int q = p + f(); // m is 17 for this call; initialized to 56 â *end example*]
|
||
|
||
[*Note [12](#note-12)*:
|
||
|
||
Except for a [*static_assert-message*](dcl.pre#nt:static_assert-message "9.1 Preamble [dcl.pre]"),
|
||
a manifestly constant-evaluated expression
|
||
is evaluated even in an unevaluated operand ([[expr.context]](expr.context#term.unevaluated.operand "7.2.3 Context dependence"))[.](#28.sentence-2)
|
||
|
||
â *end note*]
|
||
|
||
[29](#29)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L9270)
|
||
|
||
The evaluation of an expression can introduce
|
||
one or more [*injected declarations*](#def:declaration,injected "7.7 Constant expressions [expr.const]")[.](#29.sentence-1)
|
||
|
||
The evaluation is said to [*produce*](#def:produce "7.7 Constant expressions [expr.const]") the declarations[.](#29.sentence-2)
|
||
|
||
[*Note [13](#note-13)*:
|
||
|
||
An invocation of
|
||
the library function template std::meta::define_aggregate produces an injected declaration ([[meta.reflection.define.aggregate]](meta.reflection.define.aggregate "21.4.16 Reflection class definition generation"))[.](#29.sentence-3)
|
||
|
||
â *end note*]
|
||
|
||
Each such declaration has
|
||
|
||
- [(29.1)](#29.1)
|
||
|
||
an associated [*synthesized point*](#def:point,synthesized "7.7 Constant expressions [expr.const]"),
|
||
which follows the last non-synthesized program point
|
||
in the translation unit containing that declaration, and
|
||
|
||
- [(29.2)](#29.2)
|
||
|
||
an associated [*characteristic sequence*](#def:sequence,characteristic "7.7 Constant expressions [expr.const]") of values[.](#29.sentence-4)
|
||
|
||
[*Note [14](#note-14)*:
|
||
|
||
Special rules concerning reachability
|
||
apply to synthesized points ([[module.reach]](module.reach "10.7 Reachability"))[.](#29.sentence-5)
|
||
|
||
â *end note*]
|
||
|
||
[*Note [15](#note-15)*:
|
||
|
||
The program is ill-formed
|
||
if injected declarations with different characteristic sequences
|
||
define the same entity in different translation units ([[basic.def.odr]](basic.def.odr "6.3 One-definition rule"))[.](#29.sentence-6)
|
||
|
||
â *end note*]
|
||
|
||
[30](#30)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L9298)
|
||
|
||
A member of an entity defined by an injected declaration
|
||
shall not have a name reserved to the implementation ([[lex.name]](lex.name "5.11 Identifiers"));
|
||
no diagnostic is required[.](#30.sentence-1)
|
||
|
||
[31](#31)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L9303)
|
||
|
||
Let C be a [*consteval-block-declaration*](dcl.pre#nt:consteval-block-declaration "9.1 Preamble [dcl.pre]"),
|
||
the evaluation of whose corresponding expression
|
||
produces an injected declaration for an entity E[.](#31.sentence-1)
|
||
|
||
The program is ill-formed if either
|
||
|
||
- [(31.1)](#31.1)
|
||
|
||
C is enclosed by a scope associated with E or
|
||
|
||
- [(31.2)](#31.2)
|
||
|
||
letting P be a point whose immediate scope is that to which E belongs,
|
||
there is a function parameter scope or class scope
|
||
that encloses exactly one of C or P[.](#31.sentence-2)
|
||
|
||
[*Example [16](#example-16)*: struct S0 {consteval { std::meta::define_aggregate(^^S0, {}); // error: scope associated with S0 encloses the consteval block}};
|
||
|
||
struct S1;consteval { std::meta::define_aggregate(^^S1, {}); } // OKtemplate <std::meta::info R> consteval void tfn1() { std::meta::define_aggregate(R, {});}struct S2;consteval { tfn1<^^S2>(); } // OKtemplate <std::meta::info R> consteval void tfn2() {consteval { std::meta::define_aggregate(R, {}); }}struct S3;consteval { tfn2<^^S3>(); }// error: function parameter scope of tfn2<^^S3> intervenes between the declaration of S3// and the consteval block that produces the injected declarationtemplate <typename> struct TCls {struct S4; static void sfn() requires ([] {consteval { std::meta::define_aggregate(^^S4, {}); }return true; }()) { }};
|
||
|
||
consteval { TCls<void>::sfn(); } // error: TCls<void>::S4 is not enclosed by [*requires-clause*](temp.pre#nt:requires-clause "13.1 Preamble [temp.pre]") lambdastruct S5;struct Cls {consteval { std::meta::define_aggregate(^^S5, {}); } // error: S5 is not enclosed by class Cls};
|
||
|
||
struct S6;consteval { // #1struct S7; // local class std::meta::define_aggregate(^^S7, {}); // error: consteval block #1 does not enclose itself,// but encloses S7consteval { // #2 std::meta::define_aggregate(^^S6, {}); // error: consteval block #1 encloses// consteval block #2 but not S6 std::meta::define_aggregate(^^S7, {}); // OK, consteval block #1 encloses both #2 and S7}} â *end example*]
|
||
|
||
[32](#32)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L9375)
|
||
|
||
The [*evaluation context*](#def:evaluation_context "7.7 Constant expressions [expr.const]") is a set of program points
|
||
that determines the behavior of certain functions
|
||
used for reflection ([[meta.reflection]](meta.reflection "21.4 Reflection"))[.](#32.sentence-1)
|
||
|
||
During the evaluation V of an expression E as a core constant expression,
|
||
the evaluation context of an evaluation X ([[intro.execution]](intro.execution "6.10.1 Sequential execution"))
|
||
consists of the following points:
|
||
|
||
- [(32.1)](#32.1)
|
||
|
||
The program point EVAL-PT(L),
|
||
where L is the point at which E appears, and
|
||
where EVAL-PT(P), for a point P,
|
||
is a point R determined as follows:
|
||
* [(32.1.1)](#32.1.1)
|
||
|
||
If a potentially-evaluated subexpression ([[intro.execution]](intro.execution "6.10.1 Sequential execution")) of
|
||
a default member initializer I appears at P, and
|
||
a (possibly aggregate) initialization during V is using I,
|
||
then R is EVAL-PT(Q) where Q is the point at which that initialization appears[.](#32.1.1.sentence-1)
|
||
|
||
* [(32.1.2)](#32.1.2)
|
||
|
||
Otherwise, if a potentially-evaluated subexpression of
|
||
a default argument ([[dcl.fct.default]](dcl.fct.default "9.3.4.7 Default arguments")) appears at P, and
|
||
an invocation of a function ([[expr.call]](expr.call "7.6.1.3 Function call")) during V is using that default argument,
|
||
then R is EVAL-PT(Q) where Q is the point at which that invocation appears[.](#32.1.2.sentence-1)
|
||
|
||
* [(32.1.3)](#32.1.3)
|
||
|
||
Otherwise, R is P[.](#32.1.3.sentence-1)
|
||
|
||
- [(32.2)](#32.2)
|
||
|
||
Each synthesized point corresponding to an injected declaration produced by
|
||
any evaluation sequenced before X ([[intro.execution]](intro.execution "6.10.1 Sequential execution"))[.](#32.2.sentence-1)
|
||
|
||
[33](#33)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/expressions.tex#L9410)
|
||
|
||
An expression or conversion is [*potentially constant evaluated*](#def:potentially_constant_evaluated "7.7 Constant expressions [expr.const]") if it is:
|
||
|
||
- [(33.1)](#33.1)
|
||
|
||
a manifestly constant-evaluated expression,
|
||
|
||
- [(33.2)](#33.2)
|
||
|
||
a [potentially-evaluated](basic.def.odr#def:potentially_evaluated "6.3 One-definition rule [basic.def.odr]") expression,
|
||
|
||
- [(33.3)](#33.3)
|
||
|
||
an immediate subexpression of a [*braced-init-list*](dcl.init.general#nt:braced-init-list "9.5.1 General [dcl.init.general]"),[70](#footnote-70 "In some cases, constant evaluation is needed to determine whether a narrowing conversion is performed ([dcl.init.list]).")
|
||
|
||
- [(33.4)](#33.4)
|
||
|
||
an expression of the form & [*cast-expression*](expr.cast#nt:cast-expression "7.6.3 Explicit type conversion (cast notation) [expr.cast]") that occurs within a templated entity,[71](#footnote-71 "In some cases, constant evaluation is needed to determine whether such an expression is value-dependent ([temp.dep.constexpr]).") or
|
||
|
||
- [(33.5)](#33.5)
|
||
|
||
a potentially-evaluated subexpression ([[intro.execution]](intro.execution "6.10.1 Sequential execution")) of one of the above[.](#33.sentence-1)
|
||
|
||
A function or variable is[*needed for constant evaluation*](#def:needed_for_constant_evaluation "7.7 Constant expressions [expr.const]") if it is:
|
||
|
||
- [(33.6)](#33.6)
|
||
|
||
a constexpr function that[is named by](basic.def.odr#def:function,named_by_expression_or_conversion "6.3 One-definition rule [basic.def.odr]") an expression
|
||
that is potentially constant evaluated, or
|
||
|
||
- [(33.7)](#33.7)
|
||
|
||
a potentially-constant variable named by a potentially constant evaluated expression[.](#33.sentence-2)
|
||
|
||
[67)](#footnote-67)[67)](#footnoteref-67)
|
||
|
||
Overload resolution ([[over.match]](over.match "12.2 Overload resolution"))
|
||
is applied as usual[.](#footnote-67.sentence-1)
|
||
|
||
[68)](#footnote-68)[68)](#footnoteref-68)
|
||
|
||
This includes,
|
||
for example, signed integer overflow ([[expr.pre]](expr.pre "7.1 Preamble")), certain
|
||
pointer arithmetic ([[expr.add]](expr.add "7.6.6 Additive operators")), division by
|
||
zero ([[expr.mul]](expr.mul "7.6.5 Multiplicative operators")), or certain shift operations ([[expr.shift]](expr.shift "7.6.7 Shift operators"))[.](#footnote-68.sentence-1)
|
||
|
||
[69)](#footnote-69)[69)](#footnoteref-69)
|
||
|
||
Testing this condition
|
||
can involve a trial evaluation of its initializer,
|
||
with evaluations of contract assertions
|
||
using the ignore evaluation semantic ([[basic.contract.eval]](basic.contract.eval "6.11.2 Evaluation")),
|
||
as described above[.](#footnote-69.sentence-1)
|
||
|
||
[70)](#footnote-70)[70)](#footnoteref-70)
|
||
|
||
In some cases, constant evaluation is needed to determine whether a narrowing conversion is performed ([[dcl.init.list]](dcl.init.list "9.5.5 List-initialization"))[.](#footnote-70.sentence-1)
|
||
|
||
[71)](#footnote-71)[71)](#footnoteref-71)
|
||
|
||
In some cases, constant evaluation is needed to determine whether such an expression is value-dependent ([[temp.dep.constexpr]](temp.dep.constexpr "13.8.3.4 Value-dependent expressions"))[.](#footnote-71.sentence-1)
|