如何在C++库中为std::variant编程式注册用户自定义类型?
编译时注册自定义类型到std::variant的解决方案
核心思路
借助C++模板元编程和CRTP(奇异递归模板模式),实现编译时自动注册用户自定义类型,无需库提前知晓类型细节,最终将所有注册类型聚合为std::variant,同时避开dynamic_cast的运行时开销。
具体实现步骤
1. 定义基础类型列表模板
用递归模板结构存储注册类型,作为后续转换为std::variant的基础:
// 库代码:类型列表模板 template<typename... Ts> struct TypeList {}; // 初始化空的全局类型列表 using GlobalRegisteredTypes = TypeList<>;
2. 实现CRTP自动注册器
用户自定义类型通过继承该CRTP基类,自动将自身追加到全局类型列表中:
// 库代码:类型追加辅助模板 template<typename List, typename T> struct AppendType; template<typename... Ts, typename T> struct AppendType<TypeList<Ts...>, T> { using type = TypeList<Ts..., T>; }; // CRTP注册基类 template<typename Derived> struct AutoRegistrar { private: // 生成包含当前类型的新列表 using UpdatedTypeList = typename AppendType<GlobalRegisteredTypes, Derived>::type; // 利用inline变量触发类型更新(确保编译时执行) inline static const bool register_flag = [](){ // 通过类型推导固化更新后的全局列表 using CurrentGlobal = UpdatedTypeList; return true; }(); };
3. 类型列表转std::variant
提供模板将全局类型列表自动转换为对应的std::variant类型:
// 库代码:类型列表转variant的辅助模板 template<typename List> struct ListToVariant; template<typename... Ts> struct ListToVariant<TypeList<Ts...>> { using type = std::variant<Ts...>; }; // 最终对外暴露的variant类型,自动包含所有注册的自定义类型 using RegisteredVariant = typename ListToVariant<GlobalRegisteredTypes>::type;
4. 用户使用示例
用户仅需让自定义类型继承AutoRegistrar,即可完成自动注册:
// 用户代码:自定义事件类型 struct PlayerAttackEvent : AutoRegistrar<PlayerAttackEvent> { void execute(const GameState& state) { // 自定义业务逻辑 } }; // 用户代码:自定义状态类型 struct PausedState : AutoRegistrar<PausedState> { void react(const PlayerAttackEvent& event) { // 自定义响应逻辑 } };
5. 库中调用自定义方法
利用std::visit遍历variant,编译时匹配并调用用户类型的指定方法:
// 库代码:处理注册类型的通用函数 template<typename Arg> void invoke_method(RegisteredVariant& obj, const Arg& arg) { std::visit([&arg](auto& concrete_obj) { // 调用用户自定义的方法,编译时检查签名合法性 concrete_obj.execute(arg); }, obj); }
核心优势
- 零运行时类型转换:完全基于编译时推导,无
dynamic_cast的性能损耗 - 自动注册:用户无需手动维护类型清单,仅需继承基类即可完成注册
- 兼容标准容器:
RegisteredVariant可直接存入std::vector、std::queue等容器 - 编译时错误检查:若用户类型未实现指定方法或参数不匹配,编译阶段直接报错
注意事项
- 需使用C++17及以上版本(依赖inline变量、
std::visit等特性) - 确保所有注册类型在
RegisteredVariant实例化前完成定义,避免未定义行为 - 如需区分多组类型(如Event和State),可复制这套逻辑创建独立的注册器和类型列表
内容的提问来源于stack exchange,提问作者Christopher Pisz
相关产品推荐
相关产品推荐

