7 Expressions [expr]

7.7 Constant evaluation [expr.const]

7.7.6 Reflection [expr.const.reflect]

The evaluation of an expression can introduce one or more injected declarations.
The evaluation is said to produce the declarations.
[Note 1: 
An invocation of the library function template std​::​meta​::​define_aggregate produces an injected declaration ([meta.reflection.define.aggregate]).
— end note]
Each such declaration has
[Note 2: 
Special rules concerning reachability apply to synthesized points ([module.reach]).
— end note]
[Note 3: 
The program is ill-formed if injected declarations with different characteristic sequences define the same entity in different translation units ([basic.def.odr]).
— end note]
A member of an entity defined by an injected declaration shall not have a name reserved to the implementation ([lex.name]); no diagnostic is required.
Let C be a consteval-block-declaration, the evaluation of whose corresponding expression produces an injected declaration for an entity E.
The program is ill-formed if either
  • C is enclosed by a scope associated with E or
  • 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.
[Example 1: 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, {}); } // OK template <std::meta::info R> consteval void tfn1() { std::meta::define_aggregate(R, {}); } struct S2; consteval { tfn1<^^S2>(); } // OK template <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 declaration template <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 lambda struct S5; struct Cls { consteval { std::meta::define_aggregate(^^S5, {}); } // error: S5 is not enclosed by class Cls }; struct S6; consteval { // #1 struct S7; // local class std::meta::define_aggregate(^^S7, {}); // error: consteval block #1 does not enclose itself, // but encloses S7 struct S8; // local class consteval { // #2 std::meta::define_aggregate(^^S6, {}); // error: consteval block #1 encloses // consteval block #2 but not S6 std::meta::define_aggregate(^^S8, {}); // OK, consteval block #1 encloses both #2 and S8 } } — end example]
During an evaluation V ([intro.execution]) of an expression, conversion, or initialization E as a core constant expression, the point of evaluation of E during V is the program point P determined as follows:
  • If E is a potentially-evaluated subexpression of a default member initializer I, and V is the evaluation of E in the evaluation of I as an immediate subexpression of a (possibly aggregate) initialization, then P is the point of evaluation of that initialization.
    [Note 4: 
    For example, E can be an immediate invocation in a default member initializer used by an aggregate initialization appearing at P.
    — end note]
  • Otherwise, if E is a potentially-evaluated subexpression of a default argument A ([dcl.fct.default]), and V is the evaluation of E in the evaluation of A as an immediate subexpression of a function call ([expr.call]), then P is the point of evaluation of that function call.
  • Otherwise, P is the point at which E appears.
During the evaluation V of an expression E as a core constant expression, the evaluation context of an evaluation X during V is the set C of program points determined as follows:
  • If X occurs during the evaluation Y of a manifestly constant-evaluated expression, where Y occurs during V, then C is the evaluation context of X during Y.
  • Otherwise, C contains
    • the point of evaluation of E during V and each synthesized point in the instantiation context thereof and
    • each synthesized point corresponding to an injected declaration produced by any evaluation executed during V that is sequenced before X ([intro.execution]).
[Note 5: 
The evaluation context determines the behavior of certain functions used for reflection ([meta.reflection]).
— end note]
[Example 2: struct S; consteval std::size_t f(int p) { constexpr std::size_t r = /* Q */ std::meta::is_complete_type(^^S) ? 1 : 2; // #1 if (!std::meta::is_complete_type(^^S)) { // #2 std::meta::define_aggregate(^^S, {}); } return (p > 0) ? f(p - 1) : r; } consteval { if (f(1) != 2) { throw; // OK, not evaluated } }
During each evaluation of std​::​meta​::​is_complete_type(^^S) at #1 ([meta.reflection.queries]) that is executed during the evaluation of f(1) != 2, the evaluation context contains Q, but does not contain the synthesized point associated with the injected declaration of S.
However, the synthesized point is in the evaluation context of std​::​meta​::​is_complete_type(^^S) at #2 during the evaluation of f(0).
— end example]