You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++模板函数中实现编译时类型注册与检索是否可行?

编译时类型注册与实例获取方案实现

需求概述

需要实现一个Widgets类,支持:

  1. 通过模板函数addWidget<T>在编译时注册类型,生成类型列表
  2. 通过getWidget<T>获取对应类型的实例,若T未注册则触发编译错误
  3. 通过init()从编译时类型列表生成运行时的类型-实例映射

用户提供的示例代码框架:

class Widgets
{
    template<typename T>
    void addWidget()
    {
         // register T in a type list at compile time here
    }

    template<typename T>
    T& getWidget()
    {
         // compile time check to see if T is registered
         // if registered then return a reference from a runtime map
     }

     void init()
     {
         // create a runtime type->instance map from registered types
     }
}

// 使用示例:未注册SizeWidget时触发编译错误
SizeWidget& w = widgets.getWidget<SizeWidget>();

可行实现方案

以下方案基于C++17特性,无需依赖Boost,可完美满足需求:

1. 编译时类型列表与检查工具

先实现基础的编译时类型列表和类型检查元函数:

#include <type_traits>

// 编译时类型列表模板
template<typename... Ts>
struct TypeList {};

// 元函数:检查类型是否在列表中
template<typename T, typename List>
struct IsTypeInList : std::false_type {};

template<typename T, typename... Ts>
struct IsTypeInList<T, TypeList<T, Ts...>> : std::true_type {};

template<typename T, typename... Ts>
struct IsTypeInList<T, TypeList<Ts..., T>> : std::true_type {};

// 辅助变量模板,简化调用
template<typename T, typename List>
constexpr bool IsTypeInListV = IsTypeInList<T, List>::value;

2. Widgets类实现

结合编译时类型列表与运行时实例映射:

#include <unordered_map>
#include <typeindex>
#include <memory>
#include <stdexcept>

class Widgets
{
    // 封装类型列表的辅助结构体
    template<typename... Ts>
    struct RegisteredTypes : TypeList<Ts...> {};

    // 用inline静态变量存储编译时注册的类型列表(C++17特性,跨TU生效)
    inline static auto registered_types = RegisteredTypes<>{};

    // 私有辅助:扩展类型列表的模板推导函数
    template<typename T, typename List>
    static auto extend_type_list(List) -> RegisteredTypes<T, typename List::Type...>;

public:
    template<typename T>
    void addWidget()
    {
        // 编译时扩展类型列表:通过模板推导生成新的RegisteredTypes实例
        registered_types = decltype(extend_type_list<T>(registered_types)){};
    }

    template<typename T>
    T& getWidget()
    {
        // 编译时断言:未注册类型直接触发编译错误
        static_assert(IsTypeInListV<T, decltype(registered_types)>,
                      "Error: Type is not registered in Widgets");

        // 从运行时map获取实例
        auto it = widget_map.find(std::type_index(typeid(T)));
        if (it == widget_map.end())
        {
            throw std::runtime_error("Widget instance not initialized (call init() first)");
        }
        return *static_cast<T*>(it->second.get());
    }

    void init()
    {
        // 遍历编译时类型列表,创建所有注册类型的实例
        init_impl(registered_types);
    }

private:
    // 运行时实例存储map
    std::unordered_map<std::type_index, std::unique_ptr<void>> widget_map;

    // 递归遍历类型列表的初始化实现(C++17折叠表达式)
    template<typename... Ts>
    void init_impl(TypeList<Ts...>)
    {
        (add_instance<Ts>(), ...);
    }

    // 单个类型的实例创建与存储
    template<typename T>
    void add_instance()
    {
        widget_map.emplace(std::type_index(typeid(T)), std::make_unique<T>());
    }
};

3. 关键说明

  • 编译时检查:getWidget<T>中的static_assert会在编译期验证T是否在注册列表中,未注册直接报错,符合需求
  • 跨翻译单元注册:C++17的inline静态变量确保registered_types在整个程序中是唯一实例,不同TU中调用addWidget<T>会累积扩展类型列表
  • 初始化顺序:必须先调用所有addWidget<T>完成注册,再调用init()生成实例,否则getWidget<T>会找不到运行时实例
  • C++11/14兼容:若需支持更早标准,可改用静态模板变量触发注册(需显式声明Registrar<SizeWidget>::registered),但灵活性略逊

关于Boost Fusion与Stack Overflow方案的说明

  • Boost Fusion的Map是不可变的,每次插入都会生成新的类型,无法在模板函数中动态扩展注册列表,因此不适用
  • 部分Stack Overflow的编译时注册技巧依赖全局静态变量初始化,难以在模板函数内灵活触发,而上述方案通过inline静态变量结合模板推导完美解决了这个问题

内容的提问来源于stack exchange,提问作者signedbit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 04:48:16