如何使用C++26反射将结构体名称注入另一个结构体,并扩展支持可变参数模板
如何使用C++26反射将结构体名称注入另一个结构体,并扩展支持可变参数模板
嘿,这个需求刚好踩中了C++26反射的核心应用场景,咱们一步步来实现,先搞定单参数的情况,再扩展到可变参数模板。
先搞定单参数模板的场景
要实现把模板参数的结构体名称作为outer的嵌套结构体名,咱们得用到C++26的反射特性来获取类型的编译时元信息,再用这些信息动态定义嵌套结构体的名称。
先上可运行的代码:
#include <reflect> #include <iostream> struct inner1 {}; struct inner2 {}; template <class T> struct outer { // 利用C++26反射获取T的元对象,提取原类型名称来定义嵌套结构体 template <typename MetaT = std::reflect(T)> [[using meta: name_from(std::meta::name_of(MetaT))]] struct nested { static constexpr int value = 5; }; }; int main() { std::cout << outer<inner1>::inner1::value << std::endl; // 输出5 std::cout << outer<inner2>::inner2::value << std::endl; // 输出5 return 0; }
这里的核心知识点:
std::reflect(T):获取模板参数T的编译时元对象,它包含了T的所有类型信息,比如名称、基类、成员等std::meta::name_of(MetaT):从元对象中提取出原类型的名称,比如对inner1来说,拿到的就是字符串"inner1"[[using meta: name_from(...)]]:这是C++26反射的专属属性,用来指定当前实体(这里是嵌套结构体)的名称来自括号里的元表达式结果,相当于把nested这个默认名字替换成了原类型的名称,所以outer<inner1>里的嵌套结构体就变成了inner1
扩展到可变参数模板
要支持outer<inner1, inner2>这样的可变参数,咱们需要遍历所有模板参数,为每个参数都生成对应的嵌套结构体。这时候C++26的编译时for循环就派上用场了,它能帮咱们在编译时展开参数包并生成代码。
来看实现代码:
#include <reflect> #include <iostream> struct inner1 {}; struct inner2 {}; struct inner3 {}; template <class... Ts> struct outer { // 编译时遍历所有模板参数的元对象序列 template for (auto meta_t : std::reflect(Ts)...) { // 为每个元对象对应的类型生成嵌套结构体,名称沿用原类型名 [[using meta: name_from(std::meta::name_of(meta_t))]] struct nested { static constexpr int value = 5; }; } }; int main() { std::cout << outer<inner1, inner2>::inner1::value << std::endl; // 输出5 std::cout << outer<inner1, inner2>::inner2::value << std::endl; // 输出5 std::cout << outer<inner1, inner2, inner3>::inner3::value << std::endl; // 输出5 return 0; }
这里的关键细节:
std::reflect(Ts)...:把可变参数包Ts展开成对应的元对象序列,每个元对象对应一个模板参数的类型信息template for (auto meta_t : ...):C++26新增的编译时循环语法,会在编译时遍历元对象序列,为每个元对象执行一次循环体代码- 循环体里的逻辑和单参数场景一致:为每个元对象对应的类型生成同名嵌套结构体,里面包含
static constexpr int value = 5
一些需要注意的点
- 编译器支持:目前(2024年)只有GCC 14+、Clang 18+等较新版本的编译器支持C++26反射特性,编译时需要开启对应选项,比如GCC要加
-std=c++26 -freflect - 编译时开销:所有反射和展开操作都是在编译时完成的,不会带来任何运行时性能损耗
- 名称冲突:如果模板参数里有同名类型(比如不同命名空间的同名结构体),需要额外处理命名空间信息,不过你的需求里没涉及这个,暂时不用管
内容来源于stack exchange
相关产品推荐
相关产品推荐

