拼接说明符 (C++26 起)

来自cppreference.com


 
 
C++ 语言
 
 

从反射值生成一个语言构造,有效地将反射内容“拼接”到源代码中。

语法

[: 常量表达式 :] (1)
typename [: 常量表达式 :] (2)
template [: 常量表达式 :] (3)
常量表达式 - std::meta::info 类型的经转换的常量表达式
1) 拼接说明符,其含义取决于语境。
2) 拼接说明符,其为类型说明符。
3) 用于指定模板的拼接说明符。如果它不作为表达式出现,则该说明符后面必须跟一个模板实参列表(由 <> 包围),并且必须是作用域解析运算符 :: 的左操作数,例如 template[:R:]<int>::is_always_lock_free

解释

通常,拼接说明符的含义与 常量表达式 所表示的语言构造的名称或值相同。称该拼接说明符**指定**了那个语言构造。

表达式

对于作为表达式的拼接说明符,该说明符不得跟在 typename 关键字之后,后面也不得跟着作用域解析运算符 ::

struct S { static constexpr int a = 1; };
template<typename> struct TCls { static constexpr int b = 2; };

constexpr int c = [:^^S:]::a; // [:^^S:] 不是表达式
constexpr int d = template [:^^TCls:]<int>::b; // template [:^^TCls:]<int> 不是表达式
template<auto V> constexpr int e = [:V:]; // [:V:] 是表达式
constexpr int f = template [:^^e:]<^^S::a>; // template [:^^e:]<^^S::a> 是表达式

constexpr auto g = typename [:^^int:](42); // typename [:^^int:] 是类型说明符

不带括号的拼接表达式不能作为模板实参出现。

拼接表达式可以指定函数、对象、数据成员、变量、结构化绑定、值或枚举项。但不能指定构造函数、析构函数、无名位域,或者那些对其进行指名会导致被 lambda 表达式捕获的局部实体。

如果一个拼接表达式指定了变量或结构化绑定 V,那么仅当 V 具有静态或线程存储期,或者在包含该表达式的作用域内声明时,该表达式才有效。

指定非静态数据成员隐式对象成员函数的拼接表达式只能用于以下情形:

与非静态成员的名字不同,拼接表达式 [:r:] 永远不会被隐式转换为 this->[:r:]

拼接表达式 [:r:] 可以描述一个**直接基类关系** (D, B)(例如,如果 rstd::meta::bases_of(^^D, ctx) 中的一个元素)。直接基类关系只能作为类成员访问表达式的第二操作数出现。该类成员访问表达式将第一操作数转换为“(可能带有 cv 限定符的)D 的引用”,并得到转换后值的 B 直接基类子对象。这可用于访问原本会有歧义或不可访问的基类子对象。

只有当使用 template 关键字时,拼接表达式才能指定函数模板或变量模板。

类型

作为类型说明符的拼接说明符,其后不得跟着作用域解析运算符 ::,且要么前面为 typename,要么位于一个仅限类型的语境中。

template<std::meta::info R> void tfn() {
    typename [:R:]::type m; // OK,对限定名使用 typename
                            // (此处 [:R:] 是作用域,而非类型说明符。)
}
struct S { using type = int; };

void fn() {
    [:^^S::type:] *var; // 错误:[:^^S::type:] 是表达式
    typename [:^^S::type:] *var; // OK,声明 int* 类型的变量
}

using alias = [:^^S::type:]; // OK,仅限类型的语境

拼接类型说明符可以指定一个类型、一个类模板或一个别名模板。如果它指定的是模板且没有模板实参列表,则进行类模板实参推导

拼接说明符也可以出现在类型要求中。

template<typename T> concept C = requires {
    typename [:T::r1:]; // 当 T::r1 不是类型的反射时失败
    typename [:T::r2:]<int>; // 若 T::r2 不是某个模板 Z 的反射
                             // 且 Z<int> 为类型,则失败
};

作用域

对于作为作用域解析运算符 :: 左操作数的拼接说明符,如果该说明符带有模板实参列表,则其前面必须加上 typenametemplate

template<int V>
struct TCls {
    static constexpr int s = V;
    using type = int;
};

int v1 = [:^^TCls<1>:]::s;
int v2 = template [:^^TCls:]<2>::s; // OK,'template' 是拼接说明符的一部分
typename [:^^TCls:]<3>::type v3 = 3; // OK,'typename' 是 typename 说明符的一部分
template [:^^TCls:]<3>::type v4 = 4; // OK,'template' 是拼接说明符的一部分
typename template [:^^TCls:]<3>::type v5 = 5; // OK,同 v3
[:^^TCls:]<3>::type v6 = 6; // 错误:非预期的 <

拼接说明符必须指定一个类类型、枚举类型或命名空间。在 [:r:]<TArgs...>::template[:r:]<TArgs...>:: 中的拼接说明符必须指定一个类模板或别名模板。

命名空间

命名空间别名定义或using 指令中,拼接说明符 [:r:] 必须指定一个并非全局命名空间的命名空间名称或命名空间别名。待决的 r 可以出现在命名空间别名定义中(但不能出现在 using 指令中),这会导致该命名空间别名也成为待决的。

示例

参阅