反射运算符 (C++26 起)

来自cppreference.com


 
 
C++ 语言
 
 

一元 ^^ 运算符,也称反射运算符,生成给定语言构造的反射——std::meta::info 类型。

语法

^^ :: (1)
^^ 反射名 (2)
^^ 类型标识 (3)
^^ 标识表达式 (4)
反射名 - 一个标识符,可选地限定,可选地使用模板指明标记(也称模板消歧义符)。这种形式优先于 类型标识标识表达式
类型标识 - 一个类型标识
标识表达式 - 一个标识表达式

会解析语法上能构成 ^^ 操作数的最长记号序列。如果结果表示的是模板,那么表达式一定不能直接紧跟 <

static_assert(std::meta::is_type(^^int())); // ^^int() 表示类型 int()
                                            // 不是对 ^^int 的函数调用

template<bool> struct X {};
consteval bool operator<(std::meta::info, X<false>) { return false; }
consteval void g(std::meta::info r, X<false> xv) {
    r == ^^int && true;   // 错误:^^ 应用于 类型标识 int&&
    r == ^^int & true;    // 错误:^^ 应用于 类型标识 int&
    r == (^^int) && true; // 正确
    r == ^^int &&&& true; // 错误:int &&&& 不是一个有效的 类型标识
    ^^X < xv;             // 错误:表示模板的反射表达式紧跟 <
    (^^X) < xv;           // 正确
    ^^X<true> < xv;       // 正确
}

解释

1) 表达式 ^^:: 表示全局命名空间。
2)^^反射名 中,对 反射名 进行查找,并按如下方式确定其表示:
  • 如果存在template 消歧义符,则结果表示注入类名指名的类模板。
  • 否则,当把注入类名作为类型名使用时,它必须不产生歧义(否则该表达式无效)。结果表示被指名的类型。
  • 否则,如果查找发现一个重载集,该重载集必须仅包含某个唯一函数模板的声明。结果表示该函数模板。
  • 否则,如果查找发现一个类模板变量模板别名模板,则结果表示该模板。
  • 否则,如果查找发现一个类型模板形参,则结果表示对应实参的类型。
  • 否则,如果查找发现一个 typedef 名类型别名,则结果表示该类型别名。
template<typename T>
struct S {
    static constexpr std::meta::info r = ^^T;
    using type = T;
};
static_assert(S<int>::r == ^^int); // OK
static_assert(^^S<int>::type != ^^int); // OK

typedef struct X {} Y;
typedef struct Z {} Z;
constexpr std::meta::info e = ^^Y; // OK,表示类型别名 Y
constexpr std::meta::info f = ^^Z; // OK,表示类型别名 Z,而不是类型 Z
  • 否则,如果查找发现一个类类型或一个枚举类型,则结果表示该类型。
  • 否则,如果查找发现一个匿名联合体的类成员,则结果表示该成员。
  • 否则,将 反射名 视为一个 标识表达式(见如下形式 (4))。
3) 表达式 ^^类型标识 表示一个按如下方式确定的实体:
  • 如果该 类型标识 指定一个占位类型(例如 auto),则表达式无效。
  • 否则,如果该 类型标识 指名了一个带有模板实参列表的别名模板,则结果表示由此指名的类型别名。(其他类型别名形式按 反射名 对待,而且结果也将表示类型别名。)
  • 否则,结果表示该 类型别名 代表的类型。
4) 表达式 ^^标识表达式 表示一个按如下方式确定的实体:
  • 如果 标识表达式 代表:
  • 一个在 lambda 的初始化捕获中声明的变量,
  • 一个函数局部预定义变量__func__),
  • 一个在 requires 表达式中声明的局部形参,或者
  • 一个本地实体,且该实体引入点和反射表达式之间介入了一个 lambda 作用域(不论是否捕获了该实体),
那么表达式无效。
  • 否则,如果 标识表达式 指代一个重载集,那么重载决议必须选出唯一函数。结果表示该函数。
template<typename T> void fn() requires (^^T != ^^int);
template<typename T> void fn() requires (^^T == ^^int);
template<typename T> void fn() requires (sizeof(T) == sizeof(int));
constexpr std::meta::info a = ^^fn<char>; // 正确
constexpr std::meta::info b = ^^fn<int>;  // 错误:有歧义

其操作数是一个不求值操作数

注解

反射值也可以使用 std::meta::reflect_constantstd::meta::reflect_objectstd::meta::reflect_function默认初始化 std::meta::info 生成。此外,还可以调用 <meta> 中的函数并传入已有的 std::meta::info 对象来获取反射值。

当类型别名作为 类型标识 的一部分出现时,反射运算符并不会保留此类型别名。

using T = int;
static_assert(^^T != ^^int);
static_assert(^^T& == ^^int&);
static_assert(^^T const == ^^const int);

示例

int arr[] = {1, 2, 3};
auto [a1, a2, a3] = arr;
[[=1]] void fn(int n);
enum Enum { A };
using Alias = int;
struct S { int mem; };
template<auto> struct TCls {};
template<auto> void TFn();
template<auto> int TVar;
template<auto N> using TAlias = TCls<N>;
template<auto> concept Concept = true;
namespace NS {}
namespace NSAlias = NS;

constexpr auto r_arr   = ^^arr;      // 表示变量
constexpr auto r_sb    = ^^a3;       // 表示结构化绑定
constexpr auto r_fn    = ^^fn;       // 表示函数
constexpr auto r_enum  = ^^Enum::A;  // 表示枚举项
constexpr auto r_alias = ^^Alias;    // 表示类型别名
constexpr auto r_type  = ^^S;        // 表示类型
constexpr auto r_mem   = ^^S::mem;   // 表示类成员
constexpr auto r_tcls  = ^^TCls;     // 表示类模板
constexpr auto r_tfn   = ^^TFn;      // 表示函数模板
constexpr auto r_tvar  = ^^TVar;     // 表示变量模板
constexpr auto r_ttype = ^^TAlias;   // 表示别名模板
constexpr auto r_cncpt = ^^Concept;  // 表示概念
constexpr auto r_ns    = ^^NS;       // 表示命名空间
constexpr auto r_ns2   = ^^NSAlias;  // 表示命名空间别名

参阅