能否在编译时存储模板方法的参数类型供类用户访问?
问题分析与解答
原代码不符合C++规范的原因
你的实现存在多处违反C++标准的问题,核心问题如下:
- 模板参数无法推导:
run函数模板中的typename... New和typename... Old没有对应的推导上下文,调用run时编译器无法自动推导这些参数,必须显式指定,这显然违背了你想要自动累积类型的意图。 - constexpr变量不可修改:
types_<T...>是constexpr静态成员,constexpr变量默认具备const属性,无法在运行时赋值(即使是constexpr赋值也需在编译期完成,而你的赋值操作位于运行时函数体内),这行代码会直接触发编译错误。 - 静态成员的共享性:静态成员属于类而非实例,所有
User实例会共享同一组types_<T...>实例,无法实现每个实例独立存储自己的Event类型序列。
替代解决方案
根据你“存储每个实例调用run时的Event类型,供类用户访问”的需求,提供三种可行方案:
方案1:运行时类型追踪(简单易用)
如果只需要在运行时让用户查询哪些类型被传入过,直接用std::set<std::type_index>存储即可:
#include <set> #include <typeindex> #include <typeinfo> class User { public: template <typename Event> void run(const Event& event) { event_types_.insert(std::type_index(typeid(Event))); /* Do stuff.. */ } // 对外暴露已存储的类型集合 const std::set<std::type_index>& get_event_types() const { return event_types_; } private: std::set<std::type_index> event_types_; };
方案2:编译期+运行态结合的类型追踪
如果需要同时支持编译期类型访问和运行时类型记录,可以结合编译期类型列表和运行时类型索引:
#include <vector> #include <typeindex> #include <typeinfo> // 编译期类型列表,用于静态层面的类型访问 template <typename... Ts> struct TypeList {}; class User { public: template <typename Event> void run(const Event& event) { // 运行时记录类型索引 event_types_.push_back(std::type_index(typeid(Event))); /* Do stuff.. */ } // 运行时获取所有Event类型的type_index列表 const std::vector<std::type_index>& get_event_types() const { return event_types_; } // 编译期类型累积扩展:可通过外部元编程工具(如Boost.Mp11)实现动态扩展TypeList using CurrentEventTypes = TypeList<>; private: std::vector<std::type_index> event_types_; };
方案3:纯编译期类型累积(基于CRTP)
如果只需要在编译期访问累积的类型序列,可使用CRTP(奇异递归模板模式)实现实例级别的类型追踪:
template <typename... Ts> struct TypeList {}; // CRTP基类,负责编译期类型累积 template <typename Derived, typename... AccumulatedEvents> class EventTracker { public: // 对外暴露当前累积的类型列表 using EventTypes = TypeList<AccumulatedEvents...>; template <typename Event> void run(const Event& event) { // 转发到带累积类型参数的实现函数 static_cast<Derived&>(*this).template run_impl<Event>(event); /* Do stuff.. */ } protected: // 核心实现:扩展类型列表并更新实例类型 template <typename Event> void run_impl(const Event& event) { // 生成包含新Event类型的派生类版本 using NewTracker = EventTracker<Derived, AccumulatedEvents..., Event>; // 可根据需求转移实例状态到新类型对象(需注意生命周期管理) } }; // 用户类继承CRTP基类 class User : public EventTracker<User> {}; // 使用示例:编译期获取累积类型 void test() { User u; u.run(int{}); u.run(double{}); // 此时u的EventTypes为TypeList<int, double> using Events = decltype(u)::EventTypes; }
内容的提问来源于stack exchange,提问作者fra.qrt
相关产品推荐
相关产品推荐

