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 defaulted special member 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.
    [Note 1: 
    Default member initializers used to initialize a base or member subobject ([class.base.init]) are considered to be part of the function body ([dcl.fct.def.general]).
    — end note]
[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>; // OK, f<char> is not an immediate function auto b = &f<int>; // error: f<int> is an immediate function static_assert(f(3) == 6); // OK template<class T> constexpr int g(T t) { // g<int> is not an immediate function return 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)); // OK int 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], [temp.inst]) return t; } template<class T> constexpr T hh() { // hh<int> is an immediate function because of the invocation return 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 function struct 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 a return 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 behavior const int i1 = l(1); // static initialization; value of 1 or contract violation at compile time const int i2 = l(2); // dynamic initialization; undefined behavior const int i3 = l(3); // static initialization; value of 3 — end example]