反射扩展
C++ 反射扩展,ISO/IEC TS 23619:2021,指定了对核心语言的修改,并定义了本页列出的 C++ 标准库新组件。
反射 TS 基于 C++20 标准(但概念的定义是按照概念 TS的风格指定的)。
[编辑] 核心语言变更
[编辑] reflexpr-说明符
一个 reflexpr-说明符 的形式为 reflexpr
(
reflexpr-操作数 )
,并指定一个元对象类型(见下文)。
reflexpr-操作数 可以是以下之一:
::
|
(1) | ||||||||
类型标识 | (2) | ||||||||
嵌套名说明符(可选) 命名空间名 | (3) | ||||||||
id-表达式 | (4) | ||||||||
( 表达式 ) |
(5) | ||||||||
函数调用表达式 | (6) | ||||||||
函数式类型转换表达式 | (7) | ||||||||
其中 函数调用表达式 为
后缀表达式 ( 表达式列表(可选) ) |
|||||||||
而 函数式类型转换表达式 是以下执行显式转换的表达式类型
简单类型说明符 ( 表达式列表(可选) ) |
(1) | ||||||||
typename-说明符 ( 表达式列表(可选) ) |
(2) | ||||||||
简单类型说明符 大括号初始化列表 | (3) | ||||||||
typename-说明符 大括号初始化列表 | (4) | ||||||||
reflexpr-说明符 的操作数应为类型、命名空间、枚举器、变量、数据成员、函数参数、捕获实体、函数调用表达式 或 函数式类型转换表达式,以及带括号的表达式。reflexpr(::) 反射全局命名空间。
对于形式为 (
表达式 )
的 reflexpr-操作数,该 表达式 应为(可能多层括号的)函数调用表达式 或 函数式类型转换表达式。
如果一个无括号操作数可以被视为 类型标识 或 函数式类型转换表达式,则它被视为 类型标识。括号可用于区分函数式类型转换和 类型标识。例如,给定一个具有默认构造函数的类类型 X
,reflexpr(X()) 反射函数类型 X(),而 reflexpr((X())) 反射表达式 X()。
如果操作数同时指定一个别名和一个类名,则由 reflexpr-说明符表示的类型将反射该别名并满足 `reflect::Alias`。
如果操作数指定一个在块作用域中声明的名称,并且该命名实体既未被捕获也不是函数参数,则程序格式错误。
[编辑] 元对象类型
元对象类型是一种未命名、不完整的命名空间作用域类类型。当且仅当一个类型是元对象类型时,它才满足 `reflect::Object` 概念。元对象类型可能满足其他概念,具体取决于 `reflexpr` 的操作数。
反复将 `reflexpr` 应用于同一操作数是否产生相同或不同的类型是未指定的。如果元对象类型反映一个不完整的类类型,则某些类型转换无法应用。
元对象类型允许通过类型特征或类型转换检查 `reflexpr` 操作数的一些属性。
[编辑] 重载决议
如果 函数调用表达式 的 后缀表达式 是类类型,即 e 在 函数调用表达式 e(args) 中是类类型,则不应使用 后缀表达式 (e) 类型的用户定义转换函数。
如果 后缀表达式 不是类类型,它应命名一个函数,该函数是重载决议的唯一结果。
struct Functor { void operator()(int) const; using fptr_t = void(*)(std::nullptr_t); operator fptr_t() const; }; using Meta0 = reflexpr(Functor{}(0)); // OK // using Meta1 = reflexpr(Functor{}(nullptr)); // error: conversion function used
[编辑]
别名是通过 typedef 声明、别名声明或 using 声明引入的名称。
实体或别名 B
与实体或别名 A
反射相关,如果
-
A
和B
是相同的实体或别名, -
A
是变量或枚举器,且B
是A
的类型, -
A
是枚举,且B
是A
的基础类型, -
A
是类,且B
是A
的成员或基类, -
A
是指定实体B
的非模板别名, -
A
不是全局命名空间,且B
是A
的封闭类或命名空间, -
A
是带括号的表达式 (B
), -
A
是闭包类型B
的 lambda 捕获, -
A
是 lambda 捕获B
的闭包类型, -
B
是由 函数式类型转换表达式A
指定的类型, -
B
是通过重载决议为 函数调用表达式A
选择的函数, -
B
是函数A
的返回类型、参数类型或函数类型,或者 -
B
与实体或别名X
反射相关,且X
与A
反射相关。
反射关系是自反和传递的,但不是对称的。
非正式地说,B
与 A
反射相关的情况意味着 B
参与了 A
的声明或定义。
对由 reflexpr-说明符 表示的类型进行零次或多次连续的类型转换(产生元对象类型),可以检查与操作数反射相关的实体和别名;这样的元对象类型被认为反映了相应的反射相关实体或别名。
struct X; struct B { using X = ::X; typedef X Y; }; struct D : B { using B::Y; }; // ::X, but not B::X or B::Y is reflection-related to D::Y
[编辑] 杂项
- 用作 reflexpr-操作数 的表达式是未求值表达式,并且可能在编译时求值。
- 为了确定通过捕获默认值在 lambda 表达式中捕获的变量,`reflexpr` 操作数不被视为未求值操作数。
- 由元对象类型 `T` 反射的具有静态存储期的函数或变量,通过特化 std::experimental::reflect::get_pointer<T> 进行odr-use,如同通过获取提名该函数或变量的 id-expression 的地址一样。
- 可以有多个元对象类型的定义,只要所有对该类型的操作都产生相同的常量表达式结果。
- 如果一个类型是由 reflexpr-说明符表示的,并且其操作数,则该类型是依赖的:
[编辑] 关键字
[编辑] 预定义特性测试宏
__cpp_reflection (反射 TS) |
值至少为 201902 表示支持反射 TS (宏常量) |
[编辑] 库支持
[编辑] 概念
定义于头文件
<experimental/reflect> | |
定义于命名空间
std::experimental::reflect | |
定义于内联命名空间
std::experimental::reflect::v1 | |
(反射 TS) |
指定一个类型是元对象类型 (概念) |
(反射 TS) |
指定一个元对象类型是元对象序列类型 (概念) |
(反射 TS) |
指定元对象类型反映一个模板参数作用域 (概念) |
(反射 TS) |
指定一个元对象类型反映一个具有关联(可能为空)名称的实体或别名 (概念) |
(反射 TS) |
指定元对象类型反映类型别名、命名空间别名或通过 using-声明引入的别名 (概念) |
(反射 TS) |
指定元对象类型反映类的一个 成员声明 (概念) |
(反射 TS) |
指定元对象类型反映枚举器 (概念) |
(反射 TS) |
指定元对象类型反映变量或数据成员 (概念) |
(反射 TS) |
指定一个元对象类型满足 `RecordMember`、`Enumerator` 或 `Variable`,或者反映全局命名空间以外的命名空间 (概念) |
(反射 TS) |
指定元对象类型反映一个具有类型的实体 (概念) |
(反射 TS) |
指定一个元对象类型反映一个命名空间 (概念) |
(反射 TS) |
指定一个元对象类型反映全局命名空间 (概念) |
(反射 TS) |
指定元对象类型反映非联合类类型 (概念) |
(反射 TS) |
指定元对象类型反映枚举类型 (概念) |
(反射 TS) |
指定元对象类型反映类类型 (概念) |
(反射 TS) |
指定一个元对象类型反映一个命名空间、类、枚举、函数、闭包类型、模板参数作用域 (概念) |
(反射 TS) |
指定一个元对象类型反映一个类型 (概念) |
(反射 TS) |
指定一个元对象类型反映一个枚举器或一个 constexpr 变量 (概念) |
(反射 TS) |
指定元对象类型反映从 `get_base_classes` 获取的直接基类 (概念) |
(反射 TS) |
指定一个元对象类型反映一个函数参数 (概念) |
(反射 TS) |
指定元对象类型反映一个函数(包括构造函数和析构函数) (概念) |
(反射 TS) |
指定一个元对象类型反映一个表达式 (概念) |
(反射 TS) |
指定一个元对象类型反映一个带括号的表达式 (概念) |
(反射 TS) |
指定元对象类型反映一个 函数调用表达式 (概念) |
(反射 TS) |
指定一个元对象类型反映一个 函数式类型转换表达式 (概念) |
(反射 TS) |
指定一个元对象类型反映一个函数(不包括构造函数和析构函数) (概念) |
(反射 TS) |
指定元对象类型反映一个成员函数(不包括构造函数和析构函数) (概念) |
(反射 TS) |
指定一个元对象类型反映一个特殊成员函数 (概念) |
(反射 TS) |
指定一个元对象类型反映一个构造函数 (概念) |
(反射 TS) |
指定一个元对象类型反映一个析构函数 (概念) |
(反射 TS) |
指定一个元对象类型反映一个运算符函数或一个转换函数 (概念) |
(反射 TS) |
指定一个元对象类型反映一个转换函数 (概念) |
(反射 TS) |
指定元对象类型反映非泛型 lambda 的闭包类型 (概念) |
(反射 TS) |
指定一个元对象类型反映一个 lambda 捕获 (概念) |
[编辑] 元对象操作
定义于头文件
<experimental/reflect> | |
定义于命名空间
std::experimental::reflect | |
定义于内联命名空间
std::experimental::reflect::v1 | |
| |
(反射 TS) |
检查两个元对象类型是否反映相同的实体或别名 (类模板) |
(反射 TS) |
获取被反射实体或别名声明的推定行号 (类模板) |
(反射 TS) |
获取被反射实体或别名声明的实现定义的列号 (类模板) |
(反射 TS) |
获取被反射实体或别名声明的推定文件名 (类模板) |
| |
(反射 TS) |
获取元对象序列的大小 (类模板) |
(反射 TS) |
获取序列中指定索引的元对象类型 (类模板) |
(反射 TS) |
将模板应用于元对象序列 (类模板) |
| |
(反射 TS) |
检查被反射实体或别名是否未命名 (类模板) |
(反射 TS) |
获取被反射实体或别名的非限定名称 (类模板) |
(反射 TS) |
获取被反射实体或别名的实现定义的显示名称 (类模板) |
| |
(反射 TS) |
获取反映被反射别名的关联实体的元对象类型 (类模板) |
| |
(反射 TS) |
获取反映被反射实体或别名类型的元对象类型 (类模板) |
(反射 TS) |
获取被反射实体或别名的类型 (类模板) |
(反射 TS) |
检查元对象类型是否反映枚举类型 (类模板) |
(反射 TS) |
检查元对象类型是否反映联合类型 (类模板) |
检查元对象类型是否反映使用 class 或 struct 分别声明的非联合类类型 (类模板) | |
| |
(反射 TS) |
获取反映被反射实体或别名作用域的元对象类型 (类模板) |
| |
(反射 TS) |
获取反映给定基类关系中基类的元对象类型 (类模板) |
| |
(反射 TS) |
检查被反射成员或基类是否为公共的 (类模板) |
(反射 TS) |
检查被反射成员或基类是否受保护 (类模板) |
(反射 TS) |
检查被反射成员或基类是否为私有的 (类模板) |
| |
获取一个元对象序列类型,其元素反映被反射类的公共、可访问或所有数据成员 (类模板) | |
获取一个元对象序列类型,其元素反映被反射类的公共、可访问或所有成员函数 (类模板) | |
(反射 TS) |
获取一个元对象序列类型,其元素反映被反射类的所有构造函数 (类模板) |
(反射 TS) |
获取一个元对象序列类型,其元素反映在被反射类中声明的所有运算符函数和转换函数 (类模板) |
(反射 TS) |
获取反映被反射类析构函数的元对象类型 (类模板) |
获取一个元对象序列类型,其元素反映被反射类的公共、可访问或所有嵌套类型或成员 typedef (类模板) | |
获取一个元对象序列类型,其元素反映被反射类的公共、可访问或所有基类 (类模板) | |
| |
(反射 TS) |
检查被反射枚举是否为作用域枚举 (类模板) |
(反射 TS) |
获取一个元对象序列类型,其元素反映被反射枚举的枚举器 (类模板) |
(反射 TS) |
获取反映被反射枚举基础类型的元对象类型 (类模板) |
| |
(反射 TS) |
获取被反射变量的值,该值是一个常量表达式 (类模板) |
(反射 TS) |
检查变量是否使用 thread_local 声明 (类模板) |
| |
(反射 TS) |
检查被反射参数是否具有默认参数 (类模板) |
| |
(反射 TS) |
获取一个元对象序列类型,其元素反映被反射函数的参数 (类模板) |
(反射 TS) |
检查被反射函数的参数列表是否包含省略号参数 (类模板) |
(反射 TS) |
检查被反射函数是否不抛出异常 (类模板) |
(反射 TS) |
检查被反射函数是否已删除 (类模板) |
| |
(反射 TS) |
检查被反射变量或函数是否为 constexpr (类模板) |
| |
(反射 TS) |
检查被反射命名空间或函数是否为 inline (类模板) |
| |
(反射 TS) |
获取反映被反射带括号表达式的无括号表达式的元对象类型 (类模板) |
| |
(反射 TS) |
获取反映被反射 函数调用表达式 中函数的元对象类型 (类模板) |
| |
(反射 TS) |
获取反映被反射 函数式类型转换表达式 中构造函数的元对象类型 (类模板) |
| |
(反射 TS) |
获取被反射变量或函数的地址,或指向被反射非静态成员的成员指针值 (类模板) |
| |
分别检查被反射成员函数是否声明了 const、volatile、& 或 && 限定符 (类模板) | |
(反射 TS) |
检查被反射成员函数是否覆盖基类的成员函数 (类模板) |
| |
(反射 TS) |
检查被反射类或成员函数是否用 final 标记 (类模板) |
| |
(反射 TS) |
检查被反射变量是否为静态存储期,或者被反射成员函数是否为静态的 (类模板) |
| |
(反射 TS) |
检查被反射的特殊成员函数是否是隐式声明的 (类模板) |
(反射 TS) |
检查被反射的特殊成员函数在其第一次声明时是否已默认 (类模板) |
| |
(反射 TS) |
检查被反射的构造函数或转换函数是否用 explicit 声明 (类模板) |
| |
(反射 TS) |
检查被反射的成员函数是否为虚函数 (类模板) |
(反射 TS) |
检查被反射的成员函数是否为纯虚函数 (类模板) |
| |
(反射 TS) |
获取一个元对象序列类型,其元素反映被反射闭包类型的捕获 (类模板) |
检查被反射闭包类型的 lambda 表达式的捕获默认值是 = 还是 & (类模板) | |
(反射 TS) |
检查被反射闭包类型的 operator() 是否用 const 声明(类模板) |
| |
(反射 TS) |
检查被反射的 lambda 捕获是否显式捕获 (类模板) |
(反射 TS) |
检查被反射的 lambda 捕获是否是初始化捕获 (类模板) |
[编辑] 库特性测试宏
定义于头文件
<experimental/reflect> | |
__cpp_lib_reflection (反射 TS) |
值至少为 201902 表示支持反射 TS 的支持库 (宏常量) |
[编辑] 概念的满足
下表列出了反映操作数的元对象类型是否满足反射 TS 引入的概念。
分类 | reflexpr 操作数 |
满足的概念 |
---|---|---|
类型 | 指定 联合体 的 类名 | reflect::Union
|
指定 闭包类型 的 类名 | reflect::Lambda
| |
指定非联合类的 类名 | reflect::Record
| |
枚举名 | reflect::Enum
| |
模板 类型参数 | reflect::Type , reflect::Alias | |
decltype-说明符 | reflect::Type , reflect::Alias | |
通过 using-声明 引入的 类型名 | reflect::Type , reflect::Alias , reflect::ScopedMember | |
任何其他 typedef-名 | reflect::Type , reflect::Alias | |
任何其他 类型标识 | reflect::Type
| |
Namespace | 命名空间别名 | reflect::Namespace , reflect::Alias |
全局命名空间 | reflect::GlobalScope
| |
任何其他命名空间 | reflect::Namespace
| |
Expression | 数据成员的名称 | reflect::Variable
|
变量的名称 | reflect::Variable
| |
枚举器的名称 | reflect::Enumerator
| |
函数参数的名称 | reflect::FunctionParameter
| |
捕获实体的名称 | reflect::LambdaCapture
| |
带括号的表达式 | reflect::ParenthesizedExpression
| |
函数调用表达式 | reflect::FunctionCallExpression
| |
函数式类型转换表达式 | reflect::FunctionalTypeConversion
|
如果 id-expression 形式的操作数是常量表达式,则由 reflexpr-说明符指定的类型也满足 `reflect::Constant`。
如果 reflexpr-操作数指定类成员,则由 reflexpr-说明符表示的类型也满足 `reflect::RecordMember`。
[编辑] 另请参阅
包含某些类型信息的类,由 typeid 运算符返回 (类) | |
(C++11) |
编译时类型信息工具 |