[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_]

The constituent values of an object o are

The constituent references of an object o are

The constituent values and constituent references of a variable x are defined as follows:

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.

An object o is constexpr-referenceable from a point P if

[Example 1: struct A { int m;const int& r;};void f() { static int sx;thread_local int tx; int ax; A aa = {1, 2};static A sa = {3, 4};auto lambda = [] { int ay;};} — _end example_]

An object or reference x isconstexpr-representable 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.

A variable v is constant-initializable if

A constant-initializable variable is constant-initializedif either it has an initializer or its type is const-default-constructible ([dcl.init.general]).

[Example 2: void f() { int ax = 0; thread_local int tx = 0; static int sx; static int& rss = sx; static int& rst = tx; static int& rsa = ax; thread_local int& rts = sx; thread_local int& rtt = tx; thread_local int& rta = ax; int& ras = sx; int& rat = tx; int& raa = ax; } — _end example_]

A variable is potentially-constant if it is constexpr or it has reference or non-volatile 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

An object or reference ispotentially usable in constant expressions at point P if it is

An object or reference is usable in constant expressions at point Pif it is an object or reference that is potentially usable in constant expressions at P and is constexpr-representable at P.

[Example 3: struct A { int* const & r;};void f(int x) { constexpr A a = {&x};static_assert(a.r == &x); [&] { static_assert(a.r != nullptr); }();} — _end example_]

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:

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.

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

[Example 7: 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 the body of a member function of std​::​allocator<T>as defined in [allocator.members], where T is a literal type, is ignored.

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.

[Note 6:

The copy/move of the active member is trivial.

— _end note_]

For the purposes of determining whether E is a core constant expression, the evaluation of an id-expressionthat names a structured binding v ([dcl.struct.bind]) has the following semantics:

[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); constexpr auto [m] = S(1);static_assert(m == 1); using A = int[2];constexpr auto [v0, v1] = A{2, 3};static_assert(v0 + v1 == 5); } — _end example_]

During the evaluation of an expression E as a core constant expression, all id-expressions and uses of *thisthat refer to an object or reference whose lifetime did not begin with the evaluation of Eare 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.

For such an object that is not usable in constant expressions, the dynamic type of the object is constexpr-unknown.

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.

[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); } constexpr auto olympic_mile() { const int ledecky = 1500;return []{ return ledecky; };} static_assert(olympic_mile()() == 1500); struct 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); static_assert((&swam)->phelps() == 28); Swim* pswam = &swam;static_assert(pswam->phelps() == 28); static_assert(how_many(swam) == 28); static_assert(Swim().lochte() == 12); static_assert(swam.lochte() == 12); static_assert(swam.coughlin == 12); } extern Swim dc;extern Swim& trident;constexpr auto& sandeno = typeid(dc); constexpr auto& gallagher = typeid(trident); — _end example_]

An object a is said to have constant destruction if

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 7:

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 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; 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

and where the reference binding (if any) binds directly.

A contextually converted constant expression of type bool is an expression, contextually converted to bool ([conv]), 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 object or a non-immediate function, or a prvalue core constant expression whose result object ([basic.lval]) satisfies the following constraints:

[Note 9:

A glvalue core constant expression that either refers to or points to an unspecified object is not a constant expression.

— _end note_]

[Example 11: 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(); struct S { int x;constexpr S() {} };int i() { constexpr S s; } — _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 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.

[Example 12: 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 either:

An invocation 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 aggregate initialization is an immediate invocation if it evaluates a default member initializer that has a subexpression that is an immediate-escalating expression.

An expression or conversion is immediate-escalatingif it is not initially in an immediate function context and it is either

An immediate-escalating function is

An immediate-escalating expression shall appear only in an immediate-escalating function.

An immediate function is a function or constructor that is

[Example 13: 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>; auto b = &f<int>; static_assert(f(3) == 6); template<class T> constexpr int g(T t) { return t + id(42); } 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)); int x = 0;template<class T> constexpr T h(T t = id(x)) { return t;} template<class T> constexpr T hh() { return h<T>(); } int i = hh<int>(); struct A { int x;int y = id(x);};template<class T> constexpr int k(int) { return A(42).y; } constexpr int l(int c) pre(c >= 2) { return (c % 2 == 0) ? c / 0 : c;} const int i0 = l(0); const int i1 = l(1); const int i2 = l(2); const int i3 = l(3); — _end example_]

An expression or conversion is manifestly constant-evaluatedif it is:

A function or variable isneeded for constant evaluationif it is: