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 ([class.copy.elision]) is not performed, are called constant expressions.
[Note
:
Constant expressions can be evaluated during translation.
end note
]
constant-expression:
	conditional-expression
A constant initializer for a variable or temporary object o is an initializer for which interpreting its full-expression as a constant-expression results in a constant expression, except that if o is an object, such an initializer may also invoke constexpr constructors for o and its subobjects even if those objects are of non-literal class types.
[Note
:
Such a class may have a non-trivial destructor.
Within this evaluation, std::is_­constant_­evaluated() ([meta.const.eval]) returns true.
end note
]
A variable is usable in constant expressions after its initializing declaration is encountered if it is a constexpr variable, or it is of reference type or of const-qualified integral or enumeration type, and its initializer is a constant initializer.
An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine, would evaluate one of the following expressions:
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] of this document, or an invocation of the va_­start macro ([cstdarg.syn]), it is unspecified whether e is a core constant expression.
[Example
:
int x;                              // not constant
struct 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 42

constexpr int w = A(false).m;       // error: initializer for m is x, which is non-constant

constexpr 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 x
  return x;
}
constexpr int f2(int k) {
  int x = k;                        // OK: not required to be a constant expression
                                    // because x is not constexpr
  return 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 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 h(1)
end example
]
An 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.
[Note
:
Such expressions may be used as bit-field lengths, 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
:
struct A {
  constexpr A(int i) : val(i) { }
  constexpr operator int() const { return val; }
  constexpr operator long() const { return 42; }
private:
  int val;
};
template<int> struct X { };
constexpr A a = alignof(int);
alignas(a) int n;               // error: ambiguous conversion
struct B { int n : a; };        // error: ambiguous conversion
end example
]
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 and where the reference binding (if any) binds directly.
[Note
:
Such expressions may be used in new expressions, as case expressions, as enumerator initializers if the underlying type is fixed, as array bounds, and as non-type template arguments.
end note
]
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 a permitted result of a constant expression if 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
:
consteval int f() { return 42; }
consteval auto g() { return f; }
consteval int h(int (*p)() = g()) { return p(); }
constexpr int r = h();                          // OK
constexpr auto e = g();                         // ill-formed: a pointer to an immediate function is
                                                // not a permitted result of a constant expression
end example
]
[Note
:
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.83
[Example
:
bool f() {
    char array[1 + int(1 + 0.2 - 0.1 - 0.1)];   // Must be evaluated during translation
    int size = 1 + int(1 + 0.2 - 0.1 - 0.1);    // May be evaluated at runtime
    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 context if 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 invocation if it is an explicit or implicit invocation of an immediate function and is not in an immediate function context.
An immediate invocation shall be a constant expression.
[Note
:
An immediate invocation is evaluated even in an unevaluated operand.
end note
]
An expression or conversion e is manifestly constant-evaluated if it is:
  • a constant-expression, or
  • the condition of a constexpr if statement ([stmt.if]), or
  • an immediate invocation, or
  • a constraint-expression ([temp.constr.decl]) including one formed from the constraint-logical-or-expression of a requires-clause, or
  • the initializer of a variable that is usable in constant expressions or has constant initialization.84
    [Example
    :
    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 1
    double z[a];                                            // ill-formed: a is not usable
                                                            // in constant expressions
    const int b = std::is_constant_evaluated() ? 2 : y;     // static initialization to 2
    int c = y + (std::is_constant_evaluated() ? 2 : y);     // dynamic initialization to y+y
    
    constexpr int f() {
      const int n = std::is_constant_evaluated() ? 13 : 17; // n is 13
      int m = std::is_constant_evaluated() ? 13 : 17;       // m might be 13 or 17 (see below)
      char arr[n] = {}; // char[13]
      return m + sizeof(arr);
    }
    int p = f();                                            // m is 13; initialized to 26
    int q = p + f();                                        // m is 17 for this call; initialized to 56
    
    end example
    ]
An expression or conversion is potentially constant evaluated if it is:
A function or variable is needed for constant evaluation if it is:
[Example
:
struct N {
  constexpr N() {}
  N(N const&) = delete;
};
template<typename T> constexpr void bad_assert_copyable() { T t; T t2 = t; }
using ineffective = decltype(bad_assert_copyable<N>());
                        // bad_­assert_­copyable<N> is not needed for constant evaluation
                        // (and thus not instantiated)
template<typename T> consteval void assert_copyable() { T t; T t2 = t; }
using check = decltype(assert_copyable<N>());
                        // error: assert_­copyable<N> is instantiated (because it is needed for constant
                        // evaluation), but the attempt to copy t is ill-formed
end example
]
Nonetheless, implementations should provide consistent results, irrespective of whether the evaluation was performed during translation and/or during program execution.
Testing this condition may involve a trial evaluation of its initializer as described above.
Constant evaluation may be necessary to determine whether a narrowing conversion is performed ([dcl.init.list]).
Constant evaluation may be necessary to determine whether such an expression is value-dependent ([temp.dep.constexpr]).