Document Number: P0670R4, ISO/IEC JTC1 SC22 WG21 Audience: CWG, LWG Date: 2018-06-08 Authors: Matúš Chochlík (chochlik@gmail.com) Axel Naumann (axel@cern.ch) David Sankel (camior@gmail.com)

Function reflection

0. Introduction

P0194 introduced static reflection for types and variables. This paper adds static reflection of functions.

Reflection proposed here behaves as follows:

void func(int); void func(std::string); using func_call_m = reflexpr(func(123)); using func_m = get_callable_t<func_call_m>; // reflects void func(int) using param0_m = get_element_t<0, get_parameters_t<func_m>>; cout << get_name_v<get_type_t<param0_m>> << '

'; // prints "int"

The functionality introduced here allows for reflection of calls of concrete functions. This enables, for instance, GUI generation, building a catalogue for remote procedure calls, documentation generation, and signal/slot frameworks. Like P0194, this proposal omits attributes, templates, and reification (i.e. conversion of a meta object to the base level): all warrant a separate paper. We would welcome a paper especially on static reflection of attributes, matching the interface-style of P0194 and this paper! Linkage and friends will be part of a follow-up paper to P0194; they will have a combined "effect" on P0194 and this paper.

0.1 Interplay with other proposals

Most notably, this proposal relies on the Reflection TS and the Concepts TS.

P0385 discusses use cases, rationale, design decisions, and the future evolution of the proposed reflection facility. It also has usage examples and replies to frequently asked questions.

1 Scope [intro.scope]

This document is written as a set of changes against the Reflection TS (N4746). Instructions to modify or add paragraphs are written as explicit instructions. Modifications made directly to existing text from the Reflection TS use underlining to represent added text and strikethrough to represent deleted text.

2 Normative references [intro.refs]

No changes are made to Clause 2 of the Reflection TS.

3 Terms and definitions [intro.defs]

No changes are made to Clause 3 of the Reflection TS.

4 General [intro]

4.3 Acknowledgements [intro.ack]

Modify the section as follows:

This work is the result of a collaboration of researchers in industry and academia. We wish to thank people who made valuable contributions within and outside these groups, including Ricardo Fabiano de Andrade, Roland Bock, Chandler Carruth, Jackie Kay, Klaim-Joël Lamotte, Jens Maurer, and many others not named here who contributed to the discussion.

5 Lexical conventions [lex]

No changes are made to Clause 5 of the Reflection TS.

6 Basic concepts [basic]

6.2 One-definition rule [basic.def.odr]

In C++ [basic.def.odr], insert a new paragraph after the existing paragraph 8:

A function or static variable reflected by T [dcl.type.reflexpr] is odr-used by the specialization std::experimental::reflect::get_pointer<T> (21.11.4.10, 21.11.4.18), as if by taking the address of an id-expression nominating the function or variable.

In C++ [basic.def.odr], insert a new bullet (12.2.3) after (12.2.2):

or

a type implementing std::experimental::reflect::Object (21.11.3.1), as long as all operations (21.11.4) on this type yield the same constant expression results.

7 Standard conversions [conv]

No changes are made to Clause 7 of the Reflection TS.

8 Expressions [expr]

8.4 Primary expressions [expr.prim]

8.4.5 Lambda expressions [expr.prim.lambda]

8.4.5.2 Captures [expr.prim.lambda.capture]

In C++ [expr.prim.lambda.capture], apply the following change to paragraph 7:

If an expression potentially references a local entity within a declarative region in which it is odr-usable, and the expression would be potentially evaluated if the effect of any enclosing typeid expressions (8.5.1.8) or use of a reflexpr-specifier (10.1.7.6) were ignored, the entity is said to be implicitly captured by each intervening lambda-expression with an associated capture-default that does not explicitly capture it.

In C++ [expr.prim.lambda.capture], apply the following change to paragraph 11:

Every id-expression within the compound-statement of a lambda-expression that is an odr-use (6.2) of an entity captured by copy , as well as every use of an entity captured by copy in a reflexpr-operand, is transformed into an access to the corresponding unnamed data member of the closure type.

8.5 Compound expressions [expr.compound]

8.5.1 Postfix expressions [expr.post]

In C++ [expr.post], apply the following change:

postfix-expression : primary-expression postfix-expression [ expr-or-braced-init-list ] postfix-expression ( expression-list opt ) function-call-expression simple-type-specifier ( expression-list opt ) typename-specifier ( expression-list opt ) simple-type-specifier braced-init-list typename-specifier braced-init-list functional-type-conv-expression postfix-expression . template opt id-expression postfix-expression -> template opt id-expression postfix-expression . pseudo-destructor-name postfix-expression -> pseudo-destructor-name postfix-expression ++ postfix-expression -- dynamic_cast < type-id > ( expression ) static_cast < type-id > ( expression ) reinterpret_cast < type-id > ( expression ) const_cast < type-id > ( expression ) typeid ( expression ) typeid ( type-id )

function-call-expression : postfix-expression ( expression-list opt )

functional-type-conv-expression : simple-type-specifier ( expression-list opt ) typename-specifier ( expression-list opt ) simple-type-specifier braced-init-list typename-specifier braced-init-list

expression-list : initializer-list



9 Statements [stmt.stmt]

No changes are made to Clause 9 of the Reflection TS.

10 Declarations [dcl.dcl]

10.1 Specifiers [dcl.spec]

10.1.7 Type specifiers [dcl.type]

10.1.7.2 Simple type specifiers [dcl.type.simple]

In C++ [dcl.type.simple], apply the following change

...

reflexpr-operand: ::

type-id

nested-name-specifier opt identifier

nested-name-specifier opt simple-template-id

( expression )

function-call-expression

functional-type-conv-expression



10.1.7.6 Reflection type specifier [dcl.type.reflexpr]

Apply the following modification to the enumeration:

...

A is not the global namespace and B is an enclosing namespace of A , or

is not the global namespace and is an enclosing namespace of , B is the parenthesized expression ( A ) ,

A is a lambda capture of the closure type B ,

A is the closure type of the lambda capture B ,

A is the type specified by the functional-type-conv-expression B ,

A is the function selected by overload resolution for a function-call-expression B ,

A is the return type, parameter type, or function type of the function B , or

A is reflection-related to an entity or alias X and X is reflection-related to B .

For the operand :: , the type specified by the reflexpr-specifier satisfies reflect::GlobalScope . For an operand that is a parenthesized expression [expr.prim.paren], the type satisfies reflect::ParenthesizedExpression . For a parenthesized expression (E) , whether or not itself nested inside a parenthesized expression, the expression E shall be either a parenthesized expression, a function-call-expression or a functional-type-conv-expression; otherwise the program is ill-formed. For an operand of the form function-call-expression, the type satisfies reflect::FunctionCallExpression . If the postfix-expression of the function-call-expression is of class type, the function call shall not resolve to a surrogate call function ([over.call.object]). Otherwise, the postfix-expression shall name a function that is the unique result of overload resolution. For an operand of the form functional-type-conv-expression [expr.type.conv], the type satisfies reflect::FunctionalTypeConversion . [Note: The usual disambiguation between function-style cast and a type-id [dcl.ambig.res] applies. [Example: The default constructor of class X can be reflected on as reflexpr((X())) , while reflexpr(X()) reflects the type of a function returning X . — end example] — end note]

For an operand of the form identifier where identifier is a template type-parameter, the type satisfies both reflect::Type and reflect::Alias .

Modify Table 12 as follows:

Category identifier or simple-template-id kind reflect Concept type class-name designating a union reflect::Record class-name designating a closure type reflect::Lambda class-name designating a non-union class reflect::Class enum-name reflect::Enum type-name introduced by a using-declaration both reflect::Type and reflect::Alias any other typedef-name both reflect::Type and reflect::Alias namespace namespace-alias both reflect::Namespace and reflect::Alias any other namespace-name both reflect::Namespace and reflect::ScopeMember data member the name of a data member reflect::Variable value the name of a variable or structured binding that is not a local entity reflect::Variable the name of an enumerator both reflect::Enumerator and reflect::Constant the name of a function parameter reflect::FunctionParameter the name of a captured entity [expr.prim.lambda.capture] reflect::LambdaCapture

Modify the following paragraph as follows:

If the reflexpr-operand designates an entity or alias at block scope (6.3.3) or function prototype scope (6.3.4) and the entity is neither captured nor a function parameter , the program is ill-formed. If the reflexpr-operand designates a class member, the type represented by the reflexpr-specifier also satisfies reflect::RecordMember . If the reflexpr-operand designates an variable or a data member expression , it is an unevaluated operand (expr.context). If the reflexpr-operand designates both an alias and a class name, the type represented by the reflexpr-specifier reflects the alias and satisfies Alias .

11 Declarators [dcl.decl]

No changes are made to Clause 11 of the Reflection TS.

12 Classes [class]

No changes are made to Clause 12 of the Reflection TS.

13 Derived classes [class.derived]

No changes are made to Clause 13 of the Reflection TS.

14 Member access control [class.access]

No changes are made to Clause 14 of the Reflection TS.

15 Special member functions [special]

No changes are made to Clause 15 of the Reflection TS.

16 Overloading [over]

No changes are made to Clause 16 of the Reflection TS.

17 Templates [temp]

No changes are made to Clause 17 of the Reflection TS.

18 Exception handling [except]

No changes are made to Clause 18 of the Reflection TS.

19 Preprocessing directives [cpp]

No changes are made to Clause 19 of the Reflection TS.

20 Library introduction [library]

No changes are made to Clause 20 of the Reflection TS.

21 Language support library [language.support]

21.11 Static reflection [reflect]

21.11.1 In general [reflect.general]

21.11.2 Header <experimental/reflect> synopsis [reflect.synopsis]

namespace std::experimental::reflect { inline namespace v1 { // 21.11.3 Concepts for meta-object types template <class T> concept Object; template <class T> concept ObjectSequence; // refines Object template <class T> concept Named; // refines Object template <class T> concept Alias; // refines Named and ScopeMember template <class T> concept RecordMember; // refines ScopeMember template <class T> concept Enumerator; // refines Constant template <class T> concept Variable; // refines Typed and ScopeMember template <class T> concept ScopeMember; // refines Named template <class T> concept Typed; // refines Object template <class T> concept Namespace; // refines Named and Scope template <class T> concept GlobalScope; // refines Namespace template <class T> concept Class; // refines Record template <class T> concept Enum; // refines Type and Scope template <class T> concept Record; // refines Type and Scope template <class T> concept Scope; // refines Object template <class T> concept Type; // refines Named and ScopeMember template <class T> concept Constant; // refines Typed and ScopeMember template <class T> concept Base; // refines Object template <class T> concept FunctionParameter; // refines Typed and ScopeMember template <class T> concept Callable; // refines Scope and ScopeMember template <class T> concept Expression; // refines Object template <class T> concept ParenthesizedExpression; // refines Expression template <class T> concept FunctionCallExpression; // refines Expression template <class T> concept FunctionalTypeConversion; // refines Expression template <class T> concept Function; // refines Typed and Callable template <class T> concept MemberFunction; // refines RecordMember and Function template <class T> concept SpecialMemberFunction; // refines RecordMember template <class T> concept Constructor; // refines Callable and RecordMember template <class T> concept Destructor; // refines Callable and SpecialMemberFunction template <class T> concept Operator; // refines Function template <class T> concept ConversionOperator; // refines MemberFunction and Operator template <class T> concept Lambda; // refines Type and Scope template <class T> concept LambdaCapture; // refines Variable

// 21.11.4 Meta-object operations // 21.11.4.1 Multi-concept operations template < class Object T> struct is_public; template < class Object T> struct is_protected; template < class Object T> struct is_private; template <Object T> struct is_constexpr; template <Object T> struct is_static; template <Object T> struct is_final; template <Object T> struct is_explicit; template <Object T> struct is_inline; template <Object T> struct is_virtual; template <Object T> struct is_pure_virtual; template <Object T> struct get_pointer; template <class T> constexpr auto is_public_v = is_public<T>::value; template <class T> constexpr auto is_protected_v = is_protected<T>::value; template <class T> constexpr auto is_private_v = is_private<T>::value; template <class T> constexpr auto is_constexpr_v = is_constexpr<T>::value; template <class T> constexpr auto is_static_v = is_static<T>::value; template <class T> constexpr auto is_final_v = is_final<T>::value; template <class T> constexpr auto is_explicit_v = is_explicit<T>::value; template <class T> constexpr auto is_inline_v = is_inline<T>::value; template <class T> constexpr auto is_virtual_v = is_virtual<T>::value; template <class T> constexpr auto is_pure_virtual_v = is_pure_virtual<T>::value; template <class T> constexpr auto get_pointer_v = get_pointer<T>::value;

// 21.11.4.8 Record operations template <Record T> struct get_public_data_members; template <Record T> struct get_accessible_data_members; template <Record T> struct get_data_members; template <Record T> struct get_public_member_functions; template <Record T> struct get_accessible_member_functions; template <Record T> struct get_member_functions; template <Record T> struct get_public_member_types; template <Record T> struct get_accessible_member_types; template <Record T> struct get_member_types; template <Record T> struct get_constructors; template <Record T> struct get_destructor; template <Record T> struct get_operators; template <Class T> struct get_public_base_classes; template <Class T> struct get_accessible_base_classes; template <Class T> struct get_base_classes; template <Class T> struct is_final <T> ; template <Record T> using get_public_data_members_t = typename get_public_data_members<T>::type; template <Record T> using get_accessible_data_members_t = typename get_accessible_data_members<T>::type; template <Record T> using get_data_members_t = typename get_data_members<T>::type; template <Record T> using get_public_member_functions_t = typename get_public_member_functions<T>::type; template <Record T> using get_accessible_member_functions_t = typename get_accessible_member_functions<T>::type; template <Record T> using get_member_functions_t = typename get_member_functions<T>::type; template <Record T> using get_public_member_types_t = typename get_public_member_types<T>::type; template <Record T> using get_accessible_member_types_t = typename get_accessible_member_types<T>::type; template <Record T> using get_member_types_t = typename get_member_types<T>::type; template <Record T> using get_constructors_t = typename get_constructors<T>::type; template <Record T> using get_destructor_t = typename get_destructor<T>::type; template <Record T> using get_operators_t = typename get_operators<T>::type; template <Class T> using get_public_base_classes_t = typename get_public_base_classes<T>::type; template <Class T> using get_accessible_base_classes_t = typename get_accessible_base_classes<T>::type; template <Class T> using get_base_classes_t = typename get_base_classes<T>::type; template <Class T> constexpr auto is_final_v = is_final<T>::value;

// 21.11.4.10 Value operations template <Constant T> struct get_constant; template <Variable T> struct is_constexpr <T> ; template <Variable T> struct is_static <T> ; template <Variable T> struct get_pointer <T> ; template <Constant T> constexpr auto get_constant_v = get_constant<T>::value; template <Variable T> constexpr auto is_constexpr_v = is_constexpr<T>::value; template <Variable T> constexpr auto is_static_v = is_static<T>::value; template <Variable T> const auto get_pointer_v = get_pointer<T>::value; // 21.11.4.11 Base operations template <Base T> struct get_class; template <Base T> struct is_virtual <T> ; template <Base T> struct is_public<T>; template <Base T> struct is_protected<T>; template <Base T> struct is_private<T>; template <Base T> using get_class_t = typename get_class<T>::type; template <Base T> constexpr auto is_virtual_v = is_virtual<T>::value; // 21.11.4.12 Namespace operations template <Namespace T> struct is_inline <T> ; template <Namespace T> constexpr auto is_inline_v = is_inline<T>::value;

// 21.11.4.13 FunctionParameter operations template <FunctionParameter T> struct has_default_argument; template <FunctionParameter T> constexpr auto has_default_argument_v = has_default_argument<T>::value; // 21.11.4.14 Callable operations template <Callable T> struct get_parameters; template <Callable T> struct is_vararg; template <Callable T> struct is_constexpr<T>; template <Callable T> struct is_noexcept<T>; template <Callable T> struct is_inline<T>; template <Callable T> struct is_deleted; template <Callable T> using get_parameters_t = typename get_parameters<T>::type; template <Callable T> constexpr auto is_vararg_v = is_vararg<T>::value; template <Callable T> constexpr auto is_deleted_v = is_deleted<T>::value; // 21.11.4.15 ParenthesizedExpression operations template <ParenthesizedExpression T> struct get_subexpression; template <ParenthesizedExpression T> using get_subexpression_t = typename get_subexpression<T>::type; // 21.11.4.16 FunctionCallExpression operations template <FunctionCallExpression T> struct get_callable; template <FunctionCallExpression T> using get_callable_t = typename get_callable<T>::type; // 21.11.4.17 FunctionalTypeConversion operations template <FunctionalTypeConversion T> struct get_constructor; template <FunctionalTypeConversion T> using get_constructor_t = typename get_constructor<T>::type; // 21.11.4.18 Function operations template <Function T> struct get_pointer<T>; // 21.11.4.19 MemberFunction operations template <MemberFunction T> struct is_static<T>; template <MemberFunction T> struct is_const; template <MemberFunction T> struct is_volatile; template <MemberFunction T> struct has_lvalueref_qualifier; template <MemberFunction T> struct has_rvalueref_qualifier; template <MemberFunction T> struct is_virtual<T>; template <MemberFunction T> struct is_pure_virtual<T>; template <MemberFunction T> struct is_override; template <MemberFunction T> struct is_final<T>; template <MemberFunction T> constexpr auto is_const_v = is_const<T>::value; template <MemberFunction T> constexpr auto is_volatile_v = is_volatile<T>::value; template <MemberFunction T> constexpr auto has_lvalueref_qualifier_v = has_lvalueref_qualifier<T>::value; template <MemberFunction T> constexpr auto has_rvalueref_qualifier_v = has_rvalueref_qualifier<T>::value; template <MemberFunction T> constexpr auto is_override_v = is_override<T>::value; // 21.11.4.20 SpecialMemberFunction operations template <SpecialMemberFunction T> struct is_implicitly_declared; template <SpecialMemberFunction T> struct is_defaulted; template <SpecialMemberFunction T> constexpr auto is_implicitly_declared_v = is_implicitly_declared<T>::value; template <SpecialMemberFunction T> constexpr auto is_defaulted_v = is_defaulted<T>::value; // 21.11.4.21 Constructor operations template <Constructor T> struct is_explicit<T>; // 21.11.4.22 Destructor operations template <Destructor T> struct is_virtual<T>; template <Destructor T> struct is_pure_virtual<T>; // 21.11.4.23 ConversionOperator operations template <ConversionOperator T> struct is_explicit<T>; // 21.11.4.24 Lambda operations template <Lambda T> struct get_captures; template <Lambda T> struct uses_default_copy_capture; template <Lambda T> struct uses_default_reference_capture; template <Lambda T> struct is_call_operator_const; template <Lambda T> using get_captures_t = typename get_captures<T>::type; template <Lambda T> constexpr auto uses_default_copy_capture_v = uses_default_copy_capture<T>::value; template <Lambda T> constexpr auto uses_default_reference_capture_v = uses_default_reference_capture<T>::value; template <Lambda T> constexpr auto is_call_operator_const_v = is_call_operator_const<T>::value; // 21.11.4.25 LambdaCapture operations template <LambdaCapture T> struct is_explicitly_captured; template <LambdaCapture T> struct is_init_capture; template <LambdaCapture T> constexpr auto is_explicitly_captured_v = is_explicitly_captured<T>::value; template <LambdaCapture T> constexpr auto is_init_capture_v = is_init_capture<T>::value;

21.11.3 Concepts for meta-object types [reflect.concepts]

21.11.3.1 Concept Object [reflect.concepts.object] template <class T> concept Object = see below; Object<T> is satisfied true if and only if T is a meta-object type, as generated by the reflexpr operator or any of the meta-object operations that in turn generate meta-object types. 21.11.3.2 Concept ObjectSequence [reflect.concepts.objseq] template <class T> concept ObjectSequence = Object<T> && see below; ObjectSequence<T> is satisfied true if and only if T is a sequence of Object s, generated by a meta-object operation. 21.11.3.3 Concept Named [reflect.concepts.named] template <class T> concept Named = Object<T> && see below; Named<T> is satisfied true if and only if T is an Object with has an associated (possibly empty) name. 21.11.3.4 Concept Alias [reflect.concepts.alias] template <class T> concept Alias = Named<T> && ScopeMember<T> && see below; Alias<T> is satisfied true if and only if T is a Named that reflects a typedef declaration, an alias-declaration, a namespace-alias, a template type-parameter, a decltype-specifier, or a declaration introduced by a using-declaration. [Note: Any such T also satisfies ScopeMember ; its scope The Scope of an Alias is the scope that the alias was injected into. — end note]

[Example: namespace N { struct A; } namespace M { using X = N::A; } using M_X_t = reflexpr(M::X); using M_X_scope_t = get_scope_t<M_X_t>; The scope reflected by M_X_scope_t is M , not N . — end example]

Except for the type represented by the reflexpr operator, Alias properties resulting from type transformations (21.11.4) are not retained. 21.11.3.5 Concept RecordMember [reflect.concepts.recordmember] template <class T> concept RecordMember = ScopeMember<T> && see below; RecordMember<T> is satisfied true if and only if T reflects a member-declaration. Any such T also satisfies ScopeMember . 21.11.3.6 Concept Enumerator [reflect.concepts.enumerator] template <class T> concept Enumerator = Typed<T> && ScopeMember<T> && see below; Enumerator<T> is satisfied true if and only if T reflects an enumerator. Any such T also satisfies Typed and ScopeMember ; the [Note: The Scope of an Enumerator is its type also for enumerations that are unscoped enumeration types. — end note] 21.11.3.7 Concept Variable [reflect.concepts.variable] template <class T> concept Variable = Typed<T> && see below; Variable<T> is satisfied true if and only if T reflects a variable or non-static data member. Any such T also satisfies Typed . 21.11.3.8 Concept ScopeMember [reflect.concepts.scopemember] template <class T> concept ScopeMember = Named<T> && see below; ScopeMember<T> is satisfied true if and only if T satisfies RecordMember , Enumerator , or Variable , or if T reflects a namespace that is not the global namespace. Any such T also satisfies Named . [Note: The scope of members of an unnamed union is the unnamed union; the scope of enumerators is their type. — end note] 21.11.3.9 Concept Typed [reflect.concepts.typed] template <class T> concept Typed = Named<T> && see below ; Typed<T> is satisfied true if and only if T reflects an entity with a type. Any such T also satisfies Named . 21.11.3.10 Concept Namespace [reflect.concepts.namespace] template <class T> concept Namespace = Scope<T> && see below; Namespace<T> is satisfied true if and only if T reflects a namespace (including the global namespace). Any such T also satisfies Scope . [Note: Any such T that does not reflect the global namespace also satisfies ScopeMember . — end note] 21.11.3.11 Concept GlobalScope [reflect.concepts.globalscope] template <class T> concept GlobalScope = Namespace<T> && see below; GlobalScope<T> is satisfied true if and only if T reflects the global namespace. [Note: Any such T also satisfies Namespace ; it does not satisfy ScopeMember . — end note] 21.11.3.12 Concept Class [reflect.concepts.class] template <class T> concept Class = Record<T> && see below; Class<T> is satisfied true if and only if T reflects a non-union class type. Any such T also satisfies Record . 21.11.3.13 Concept Enum [reflect.concepts.enum] template <class T> concept Enum = Type<T> && Scope<T> && see below; Enum<T> is satisfied true if and only if T reflects an enumeration type. Any such T also satisfies Type and Scope . 21.11.3.14 Concept Record [reflect.concepts.record] template <class T> concept Record = Type<T> && Scope<T> && see below; Record<T> is satisfied true if and only if T reflects a class type. Any such T also satisfies Type and Scope . 21.11.3.15 Concept Scope [reflect.concepts.scope] template <class T> concept Scope = Object<T> && see below Namespace<T> || Record<T> || Enum<T> ; Scope<T> is satisfied true if and only if T reflects a namespace (including the global namespace), class, or enumeration , function or closure type . [Note: Any such T that does not reflect the global namespace also satisfies ScopeMember . — end note] 21.11.3.16 Concept Type [reflect.concepts.type] template <class T> concept Type = Named<T> && ScopeMember<T> && see below; Type<T> is satisfied true if and only if T reflects a type. Any such T also satisfies Named and ScopeMember . 21.11.3.17 Concept Constant [reflect.concepts.const] template <class T> concept Constant = ScopeMember<T> && Typed<T> && see below; Constant<T> is satisfied true if and only if T reflects a constant expression ([expr.const]). Any such T also satisfies ScopeMember and Typed . 21.11.3.18 Concept Base [reflect.concepts.base] template <class T> concept Base = Object<T> && see below; Base<T> is satisfied true if and only if T reflects a direct base class, as returned by the template get_base_classes .

Add the following paragraphs at the end of [reflect.concepts]:

21.11.3.19 Concept FunctionParameter [reflect.concepts.fctparam] template <class T> concept FunctionParameter = Typed<T> && ScopeMember<T> && see below; FunctionParameter<T> is true if and only if T reflects a function parameter. [Note: The Scope of a FunctionParameter is the Callable to which this parameter appertains. — end note]

[Note: A FunctionParameter does not satisfy Variable , and thus does not offer an interface for getting the pointer to a parameter. — end note] 21.11.3.20 Concept Callable [reflect.concepts.callable] template <class T> concept Callable = ScopeMember<T> && Scope<T> && see below; Callable<T> is true if and only if T reflects a function, including constructors and destructors. 21.11.3.21 Concept Expression [reflect.concepts.expr] template <class T> concept Expression = Object<T> && see below; Expression<T> is true if and only if T reflects an expression [expr]. 21.11.3.22 Concept ParenthesizedExpression [reflect.concepts.expr.paren] template <class T> concept ParenthesizedExpression = Expression<T> && see below; ParenthesizedExpression<T> is true if and only if T reflects a parenthesized expression [expr.prim.paren]. 21.11.3.23 Concept FunctionCallExpression [reflect.concepts.expr.fctcall] template <class T> concept FunctionCallExpression = Expression<T> && see below; FunctionCallExpression<T> is true if and only if T reflects a function-call-expression [expr.call]. 21.11.3.24 Concept FunctionalTypeConversion [reflect.concepts.expr.type.fctconv] template <class T> concept FunctionalTypeConversion = Expression<T> && see below; FunctionalTypeConversion<T> is true if and only if T reflects a functional-type-conv-expression [expr.type.conv]. 21.11.3.25 Concept Function [reflect.concepts.fct] template <class T> concept Function = Callable<T> && Typed<T> && see below; Function<T> is true if and only if T reflects a function, excluding constructors and destructors. 21.11.3.26 Concept MemberFunction [reflect.concepts.memfct] template <class T> concept MemberFunction = RecordMember<T> && Function<T> && see below; MemberFunction<T> is true if and only if T reflects a member function, excluding constructors and destructors. 21.11.3.27 Concept SpecialMemberFunction [reflect.concepts.specialfct] template <class T> concept SpecialMemberFunction = RecordMember<T> && see below; SpecialMemberFunction<T> is true if and only if T reflects a special member function [special]. 21.11.3.28 Concept Constructor [reflect.concepts.ctor] template <class T> concept Constructor = Callable<T> && RecordMember<T> && see below; Constructor<T> is true if and only if T reflects a constructor. [Note: Some types that satisfy Constructor also satisfy SpecialMemberFunction . — end note] 21.11.3.29 Concept Destructor [reflect.concepts.dtor] template <class T> concept Destructor = Callable<T> && SpecialMemberFunction<T> && see below; Destructor<T> is true if and only if T reflects a destructor. 21.11.3.30 Concept Operator [reflect.concepts.oper] template <class T> concept Operator = Function<T> && see below; Operator<T> is true if and only if T reflects an operator function [over.oper] or a conversion function [class.conv.fct]. [Note: Some types that satisfy Operator also satisfy MemberFunction or SpecialMemberFunction . — end note] 21.11.3.31 Concept ConversionOperator [reflect.concepts.convfct] template <class T> concept ConversionOperator = Operator<T> && MemberFunction<T> && see below; ConversionOperator<T> is true if and only if T reflects a conversion function [class.conv.fct]. 21.11.3.32 Concept Lambda [reflect.concepts.lambda] template <class T> concept Lambda = Type<T> && Scope<T> && see below; Lambda<T> is true if and only if T reflects a closure object (excluding generic lambdas). 21.11.3.33 Concept LambdaCapture [reflect.concepts.lambdacapture] template <class T> concept LambdaCapture = Variable<T> && see below; LambdaCapture<T> is true if and only if T reflects a lambda capture as introduced by the capture list or by capture defaults. [Note: The Scope of a LambdaCapture is its immediately enclosing Lambda . — end note]

21.11.4 Meta-object Operations [reflect.ops]

Add two new final paragraphs to [reflect.ops]:

Some operations specify result types with a nested type called type that satisfies one of the concepts in reflect . These nested types will possibly satisfy other concepts, for instance more specific ones, or independent ones, as applicable for the entity reflected by the nested type. [Example: struct X {}; X x; using x_t = get_type_t<reflexpr(x)>; While get_type_t is specified to be a Type , x_t will even satisfy Class . — end example] Whileis specified to be awill even satisfy. — end example] If subsequent specializations of operations on the same reflected entity could give different constant expression results (for instance for get_name_v because the parameter's function is redeclared with a different parameter name between the two points of instantiation), the program is ill-formed, no diagnostic required. [Example: void func(int a); auto x1 = get_name_v<get_element_t<0, get_parameters_t<reflexpr(func(42))>>>; void func(int b); auto x2 = get_name_v<get_element_t<0, get_parameters_t<reflexpr(func(42))>>>; // ill-formed, no diagnostic required — end example] — end example]

21.11.4.1 Multi-concept operations [reflect.ops.over]

Remove the section 21.11.4.1.

21.11.4.4 Named operations [reflect.ops.named]

Modify the relevant part of [reflect.ops.over] as follows:

... for T reflecting a class data member, its unqualified name . ; for T reflecting a function, its unqualified name; for T reflecting a specialization of a template function, its template-name; for T reflecting a function parameter, its unqualified name; for T reflecting a constructor, the injected-class-name of its class; for T reflecting a destructor, the injected-class-name of its class, prefixed by the character '~'; for T reflecting an operator function, the operator element of the relevant operator-function-id; for T reflecting an conversion function, the same characters as get_name_ve<R> , with R reflecting the type represented by the conversion-type-id.

In all other cases (for instance for T reflecting a lambda object) , the string's value is the empty string for get_name <T> and implementation-defined for get_display_name <T> .

Remarks: Subsequent specializations of get_name<T> on the same reflected function parameter can render the program ill-formed, no diagnostic required (21.11.4).

21.11.4.7 Member operations [reflect.ops.member]

Modify the relevant part of [reflect.ops.over] as follows:

With ST being the scope of the declaration of the entity or , typedef or value reflected by T , S is found as the innermost scope enclosing ST that is either a namespace scope (including global scope), class scope, or enumeration scope , function scope (for the function's parameters), or immediately enclosing closure type (for lambda captures). For members of an unnamed union, this innermost scope is the unnamed union. For enumerators of unscoped enumeration types, this innermost scope is their enumeration type .

21.11.4.8 Record operations [reflect.ops.record]

Modify the relevant part of [reflect.ops.record] as follows:

template <Record T> struct get_public_data_members;

template <Record T> struct get_accessible_data_members;

template <Record T> struct get_data_members;

template <Record T> struct get_public_member_functions;

template <Record T> struct get_accessible_member_functions;

template <Record T> struct get_member_functions; All specializations of these templates shall meet the TransformationTrait requirements ([meta.rqmts]). The nested type named type is an alias to an ObjectSequence specialized with RecordMember types that reflect the following subset of non-template data members of the class reflected by T : for get_data_members ( get_member_functions ) , all data (function, including constructor and destructor) members.

, all data members. for get_public_data_members ( get_public_member_functions ) , all public data (function, including constructor and destructor) members;

, all public data members; for get_accessible_data_members ( get_accessible_member_functions ) , all data (function, including constructor and destructor) members that are accessible from the context where the reflexpr-specifier appeared which (directly or indirectly) generated T .

[Example: class X { int a; friend struct Y; }; struct Y { using X_t = reflexpr(X); }; using X_mem_t = get_accessible_data_members_t<Y::X_t>; static_assert(get_size_v<X_mem_t> == 1); // passes. — end example] The order of the elements in the ObjectSequence is the order of the declaration of the data members in the class reflected by T . Remarks: The program is ill-formed if T reflects a closure type.

template <Record T> struct get_constructors;

template <Record T> struct get_destructor;

template <Record T> struct get_operators; All specializations of these templates shall meet the TransformationTrait requirements ([meta.rqmts]). The nested type named type is an alias to an ObjectSequence specialized with RecordMember types that reflect the following subset of function members of the class reflected by T : for get_constructors , all constructors.

, all constructors. for get_destructor , the destructor;

, the destructor; for get_operators , all conversion functions [class.conv.fct] and operator functions [over.oper]. The order of the elements in the ObjectSequence is the order of the declaration of the members in the class reflected by T . Remarks: The program is ill-formed if T reflects a closure type.

Modify the relevant part of [reflect.ops.record] as follows:

template <Class T> struct is_final <T> ; All specializations of is_final<T> shall meet the UnaryTypeTrait requirements ([meta.rqmts]). If T reflects a class that is marked with the class-virt-specifier final , the base characteristic of the respective template specialization is true_type , otherwise it is false_type .

21.11.4.10 Value operations [reflect.ops.value]

Modify the relevant part of [reflect.ops.value] as follows:

template <Variable T> struct is_constexpr <T> ; All specializations of is_constexpr<T> shall meet the UnaryTypeTrait requirements ([meta.rqmts]). If T reflects a variable declared with the decl-specifier constexpr , the base characteristic of the respective template specialization is true_type , otherwise it is false_type . template <Variable T> struct is_static <T> ; All specializations of is_static<T> shall meet the UnaryTypeTrait requirements ([meta.rqmts]). If T reflects a variable with static storage duration, the base characteristic of the respective template specialization is true_type , otherwise it is false_type . template <Variable T> struct get_pointer <T> ; All specializations of get_pointer<T> shall meet the UnaryTypeTrait requirements ([meta.rqmts]), with a static data member named value of type X and value x , where for variables with static storage duration: X is add_pointer<Y> , where Y is the type of the variable reflected by T and x is the address of that variable; otherwise,

is , where is the type of the variable reflected by and is the address of that variable; otherwise, X is the pointer-to-member type of the member variable reflected by T and x a pointer to the member. [Note: A FunctionParameter does not satisfy Variable , and thus does not offer an interface for getting the pointer to a parameter. — end note]

21.11.4.11 Base operations [reflect.ops.derived]

Modify the relevant part of [reflect.ops.derived] as follows:

template <Base T> struct get_class; All specializations of get_class<T> shall meet the TransformationTrait requirements ([meta.rqmts]). The nested type named type is an alias to reflexpr(X) , where X is the base class reflected by T . template <Base T> struct is_virtual <T> ;

template <Base T> struct is_public<T>;

template <Base T> struct is_protected<T>;

template <Base T> struct is_private<T>;

21.11.4.12 Namespace operations [reflect.ops.namespace]

Modify the relevant part of [reflect.ops.derived] as follows:

template <Namespace T> struct is_inline <T> ; All specializations of is_inline<T> shall meet the UnaryTypeTrait requirements ([meta.rqmts]). If T reflects an inline namespace, the base characteristic of the template specialization is true_type , otherwise it is false_type .

Add the following paragraphs at the end of [reflect.ops]:

21.11.4.13 FunctionParameter operations [reflect.ops.fctparam] template <FunctionParameter T> struct has_default_argument; All specializations of this template shall meet the UnaryTypeTrait requirements ([meta.rqmts]). If T reflects a parameter with a default argument, the base characteristic of has_default_argument<T> is true_type , otherwise it is false_type . Remarks: Subsequent specializations of has_default_argument<T> on the same reflected function parameter can render the program ill-formed, no diagnostic required (21.11.4). 21.11.4.14 Callable operations [reflect.ops.callable] template <Callable T> struct get_parameters; All specializations of this template shall meet the TransformationTrait requirements ([meta.rqmts]). The nested type named type is an alias to an ObjectSequence specialized with FunctionParameter types that reflect the parameters of the function reflected by T . If that function's parameter-declaration-clause [dcl.fct] terminates with an ellipsis, the ObjectSequence does not contain any additional elements reflecting that. The is_vararg_v<Callable> trait can be used to determine if the terminating ellipsis is in its parameter list. template <Callable T> struct is_vararg;

template <Callable T> struct is_constexpr<T>;

template <Callable T> struct is_noexcept<T>;

template <Callable T> struct is_inline<T>;

template <Callable T> struct is_deleted; All specializations of these templates shall meet the UnaryTypeTrait requirements ([meta.rqmts]). If their template parameter reflects an entity with an ellipsis terminating the parameter-declaration-clause [dcl.fct] (for is_vararg ), or an entity that is (where applicable implicitly or explicitly) declared as constexpr (for is_constexpr ), noexcept (for is_noexcept ), as an inline function [dcl.inline] (for is_inline ), or as deleted (for is_deleted ), the base characteristic of the respective template specialization is true_type , otherwise it is false_type . Remarks: Subsequent specializations of is_inline<T> on the same reflected function can render the program ill-formed, no diagnostic required (21.11.4). 21.11.4.15 ParenthesizedExpression operations [reflect.ops.expr.paren] template <ParenthesizedExpression T> struct get_subexpression; All specializations of get_subexpression<T> shall meet the TransformationTrait requirements ([meta.rqmts]). The nested type named type is the Expression type reflecting the expression E of the parenthesized expression (E) reflected by T . 21.11.4.16 FunctionCallExpression operations [reflect.ops.expr.fctcall] template <FunctionCallExpression T> struct get_callable; All specializations of get_callable<T> shall meet the TransformationTrait requirements ([meta.rqmts]). The nested type named type is the Callable type reflecting the function invoked by the function-call-expression which is reflected by T . 21.11.4.17 FunctionalTypeConversion operations [reflect.ops.expr.fcttypeconv] template <FunctionalTypeConversion T> struct get_constructor; All specializations of get_converting_callable<T> shall meet the TransformationTrait requirements ([meta.rqmts]). For a type conversion reflected by T , the nested type named type is the Constructor reflecting the constructor of the type specified by the type conversion, as selected by overload resolution. The program is ill-formed if no such constructor exists. [Note: For instance fundamental types (6.7) do not have constructors. — end note] 21.11.4.18 Function operations [reflect.ops.fct] template <Function T> struct get_pointer<T>; All specializations of get_pointer<T> shall meet the UnaryTypeTrait requirements ([meta.rqmts]), with a static data member named value of type X and value x , where for non-static member functions, X is the pointer-to-member-function type of the member function reflected by T and x a pointer to the member function; otherwise,

is the pointer-to-member-function type of the member function reflected by and a pointer to the member function; otherwise, X is add_pointer<Y> , where Y is the type of the function reflected by T and x is the address of that function. 21.11.4.19 MemberFunction operations [reflect.ops.memfct] template <MemberFunction T> struct is_static<T>;

template <MemberFunction T> struct is_const;

template <MemberFunction T> struct is_volatile;

template <MemberFunction T> struct has_lvalueref_qualifier;

template <MemberFunction T> struct has_rvalueref_qualifier;

template <MemberFunction T> struct is_virtual<T>;

template <MemberFunction T> struct is_pure_virtual<T>;

template <MemberFunction T> struct is_override;

template <MemberFunction T> struct is_final<T>;

All specializations of these templates shall meet the UnaryTypeTrait requirements ([meta.rqmts]). If their template parameter reflects a member function that is static (for is_static ), const (for is_const ), volatile (for is_volatile ), declared with a ref-qualifier & (for has_lvalueref_qualifier ) or && (for has_rvalueref_qualifier ), implicitly or expicitly virtual (for is_virtual ), pure virtual (for is_pure_virtual ), or marked with override (for is_override ) or final (for is_final ), the base characteristic of the respective template specialization is true_type , otherwise it is false_type . 21.11.4.20 SpecialMemberFunction operations [reflect.ops.specialfct] template <SpecialMemberFunction T> struct is_implicitly_declared;

template <SpecialMemberFunction T> struct is_defaulted;

All specializations of these templates shall meet the UnaryTypeTrait requirements ([meta.rqmts]). If their template parameter reflects a special member function that is implicitly declared (for is_implicitly_declared ) or that is defaulted in its first declaration (for is_defaulted ), the base characteristic of the respective template specialization is true_type , otherwise it is false_type . 21.11.4.21 Constructor operations [reflect.ops.ctor] template <Constructor T> struct is_explicit<T>; All specializations of this template shall meet the UnaryTypeTrait requirements ([meta.rqmts]). If the template parameter reflects an explicit constructor, the base characteristic of the respective template specialization is true_type , otherwise it is false_type . 21.11.4.22 Destructor operations [reflect.ops.dtor] template <Destructor T> struct is_virtual<T>;

template <Destructor T> struct is_pure_virtual<T>; All specializations of these templates shall meet the UnaryTypeTrait requirements ([meta.rqmts]). If the template parameter reflects a virtual (for is_virtual ) or pure virtual (for is_pure_virtual ) destructor, the base characteristic of the respective template specialization is true_type , otherwise it is false_type . 21.11.4.23 ConversionOperator operations [reflect.ops.convfct] template <ConversionOperator T> struct is_explicit<T>; All specializations of this template shall meet the UnaryTypeTrait requirements ([meta.rqmts]). If the template parameter reflects an explicit conversion function, the base characteristic of the respective template specialization is true_type , otherwise it is false_type . 21.11.4.24 Lambda operations [reflect.ops.lambda] template <Lambda T> struct get_captures; All specializations of this template shall meet the TransformationTrait requirements ([meta.rqmts]). The nested type named type is an alias to an ObjectSequence specialized with LambdaCapture types that reflect the captures of the closure object reflected by T . The elements are in order of appearance in the lambda-capture; captures captured because of a capture-default have no defined order among the default captures. template <Lambda T> struct uses_default_copy_capture;

template <Lambda T> struct uses_default_reference_capture; All specializations of these templates shall meet the UnaryTypeTrait requirements ([meta.rqmts]). If the template parameter reflects a closure object with a capture-default that is = (for uses_default_copy_capture ) or & (for uses_default_reference_capture ), the base characteristic of the respective template specialization is true_type , otherwise it is false_type . template <Lambda T> struct is_call_operator_const; All specializations of this template shall meet the UnaryTypeTrait requirements ([meta.rqmts]). If the template parameter reflects a closure object with a const function call operator, the base characteristic of the respective template specialization is true_type , otherwise it is false_type . 21.11.4.25 LambdaCapture operations [reflect.ops.lambdacapture] template <LambdaCapture T> struct is_explicitly_captured; All specializations of this template shall meet the UnaryTypeTrait requirements ([meta.rqmts]). If the template parameter reflects an explicitly captured entity, the base characteristic of the respective template specialization is true_type , otherwise it is false_type . template <LambdaCapture T> struct is_init_capture; All specializations of this template shall meet the UnaryTypeTrait requirements ([meta.rqmts]). If the template parameter reflects an init-capture, the base characteristic of the respective template specialization is true_type , otherwise it is false_type .

Revision history

Revision 3 (P0670R3)

First revision with formal wording.

Changes first appearing in P0670R4

Identified "constructor calls" as FunctionalTypeConversion .

. Introduced ParenthesizedExpression as disambiguation mechanism (type-id vs cast expression).

as disambiguation mechanism (type-id vs cast expression). Introduced functional-type-conv-expression.

Objects with function call operator are not Function s anymore.

s anymore. Better definition of concepts, enabling subsummation and clarifying that all concepts have only syntactic (i.e. checkable) requirements.

Specify the odr-rule behavior.

Specify that lambda captures used as reflexpr-operand are captured.

Repeated specializations of operations yielding different constant expression results are now ill-formed ndr.

Specify that get_pointer creates an odr-use.

creates an odr-use. Replaced ellipsis FunctionParameter by is_vararg Callable operation.

by operation. Rename has_default_value to has_default_argument .

References

1. Static reflection. Rationale, design and evolution. p0385