如何在编译时获取类的嵌套类列表并应用于模板参数包?
获取类的嵌套类型作为模板参数包的解决方案
好问题!不过很遗憾,直到C23,标准C都没有原生支持自动枚举一个类的所有嵌套类型的能力。不过我们有几种靠谱的替代方案,能满足你的需求,一起来看看:
方案一:手动注册嵌套类型(兼容性最好)
这是最直接、兼容性拉满的方式——我们手动把需要的嵌套类型整理成一个类型列表(比如用std::tuple),再通过辅助模板把列表展开为模板参数包。
示例代码:
#include <variant> #include <tuple> class Foo { public: class FirstNested {}; class SecondNested {}; // 手动维护嵌套类型的类型列表,清晰可控 using NestedTypes = std::tuple<FirstNested, SecondNested>; }; // 辅助模板:把tuple的类型展开,应用到目标模板 template <class Tuple, template <class...> class Template> struct ApplyTuple; template <class... Ts, template <class...> class Template> struct ApplyTuple<std::tuple<Ts...>, Template> { using type = Template<Ts...>; }; // 简化别名,方便使用 template <class Tuple, template <class...> class Template> using ApplyTuple_t = typename ApplyTuple<Tuple, Template>::type; // 现在可以这样调用 ApplyTuple_t<Foo::NestedTypes, UsesFooNestedTypes> fooNestedTypesUser;
这种方式的好处是兼容所有C++版本,而且你可以精准控制哪些嵌套类型被包含(比如排除内部辅助类),完全不会出错。
方案二:用ADL配合标签类型(更灵活的分离式写法)
如果你不想把类型列表写在类内部,可以用ADL(参数依赖查找)配合标签类型来实现外部的类型列表注册:
#include <variant> #include <tuple> // 定义一个空的标签类型,用于触发ADL struct get_nested_types_tag {}; class Foo { public: class FirstNested {}; class SecondNested {}; }; // 为Foo特化,提供它的嵌套类型列表 auto operator*(get_nested_types_tag, Foo) { return std::tuple<Foo::FirstNested, Foo::SecondNested>{}; } // 复用之前的ApplyTuple辅助模板 template <class Tuple, template <class...> class Template> struct ApplyTuple; template <class... Ts, template <class...> class Template> struct ApplyTuple<std::tuple<Ts...>, Template> { using type = Template<Ts...>; }; template <class Tuple, template <class...> class Template> using ApplyTuple_t = typename ApplyTuple<Tuple, Template>::type; // 封装成更直观的别名 template <class T, template <class...> class Template> using UsesNestedTypesOf = ApplyTuple_t<decltype(*(get_nested_types_tag{}, T{})), Template>; // 使用方式更接近你的预期 UsesNestedTypesOf<Foo, UsesFooNestedTypes> fooNestedTypesUser;
这种写法把类型列表和类本身分离,适合需要在外部统一管理类型列表的场景,不过本质还是需要手动维护类型列表。
方案三:等待C++26的反射特性(未来可期)
如果能等的话,C++26的反射提案(比如P2996)会引入编译时反射能力,到时候就能自动枚举类的嵌套类型了。大致的假想用法如下:
// 仅为示例,基于C++26反射提案的假想代码 #include <reflect> #include <variant> // 过滤出类T的所有嵌套类类型 template <class T> using get_nested_types_of = std::meta::filter<std::meta::members_of<T>, std::meta::is_class>; // 直接实现你想要的调用方式 UsesFooNestedTypes<get_nested_types_of<Foo>> fooNestedTypesUser;
不过目前这个特性还在标准化阶段,暂时没法用到生产代码里。
针对你补充的状态模式场景的小建议
你提到后来创建了更具体的状态模式相关问题——其实手动注册的方式反而更适合这种场景:状态类通常不会频繁新增,维护成本很低,而且你可以精准控制哪些状态被纳入std::variant,避免把内部辅助类也加进去,反而更可控。
内容的提问来源于stack exchange,提问作者user18754779
相关产品推荐
相关产品推荐

