◐ Shell
clean mode source ↗

[expr.const.imm]

7 Expressions [expr]

7.7 Constant evaluation [expr.const]

7.7.5 Immediate functions [expr.const.imm]

An expression or conversion is in an immediate function context if it is potentially evaluated and either:

  • its innermost enclosing non-block scope is a function parameter scope of an immediate function,
  • it is a subexpression of a manifestly constant-evaluated expression or conversion, or
  • its enclosing statement is enclosed ([stmt.pre]) by the compound-statement of a consteval if statement ([stmt.if]).

An invocation is an immediate invocation if 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.

A potentially evaluated expression or conversion is immediate-escalating if it is neither initially in an immediate function context nor a subexpression of an immediate invocation, and

An immediate-escalating function is

  • the call operator of a lambda that is not declared with the consteval specifier,
  • a non-user-provided defaulted function that is not declared with the consteval specifier, or
  • a function that is not a prospective destructor and that results from the instantiation of a templated entity defined with the constexpr specifier.

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

An immediate function is a function that is

  • declared with the consteval specifier,
  • an immediate-escalating function whose type is consteval-only ([basic.types.general]), or
  • an immediate-escalating function F whose function body contains either
    • an immediate-escalating expression or
    • a definition of a non-constexpr variable with consteval-only type
    whose innermost enclosing non-block scope is F's function parameter scope.

[Example 1: 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]