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

C++中如何在运行时通过预存的typeid(T)实现void*类型转换

问题

我使用C++实现了一套资源系统,代码如下:

template<typename T>
class Resource{
public:
    Resource() {}
    void push(std::shared_ptr<T> pRes) {
        _items.emplace(_nextId++, pRes);
    }
    friend class ResourceManager;
private:
    size_t _nextId{ 0 };
    std::unordered_map<size_t, std::shared_ptr<T>> _items;
};
class ResourceManager{
public:
    ResourceManager() {}
    ~ResourceManager() {
        for (auto& pair : _resources) delete pair.second;
    }
    template<typename T>
    void register() {
        Resource<T>* pRes = new Resource<T>;
        _resources.emplace(typeid(T).name(), (void*)pRes);
    }
    template<typename T>
    Resource<T>& getResource(){
        return *(T*)_resources[typeid(T).name()];
    }
private:
    std::unordered_map<std::string, void*> _resources;
};

目前我希望为ResourceManager类新增方法,以修改_resources中存储的所有已注册资源。请问如何借助预先存储的typeid(T).name()完成void*指针的类型转换?


解答

首先明确结论:仅靠typeid(T).name()返回的字符串,无法在运行时完成void*的安全类型转换。核心原因有两个:

  • typeid(T).name()的返回值是编译器实现定义的字符串,没有统一格式,C标准也没有提供从类名字符串反向还原C类型的内置能力。
  • C++是静态类型语言,所有指针类型转换必须在编译阶段明确目标类型,运行时不存在“根据字符串自动匹配类型做转换”的机制。

你当前的实现还有两个显性问题,需要先修正:

  1. register是C++关键字,不能作为函数名,会直接编译失败。
  2. getResource中的类型转换逻辑错误:你存入_resources的是Resource<T>*类型指针,却强转为T*再解引用,属于未定义行为,运行时必然出问题。

要实现遍历修改所有已注册资源的需求,不需要在遍历阶段临时做类型转换,只需要在注册资源的编译阶段,把对应类型的操作提前包装为类型擦除的可调用对象存起来即可,参考实现如下:

#include <functional>
#include <unordered_map>
#include <memory>
#include <string>
#include <typeinfo>

template<typename T>
class Resource{
public:
    Resource() {}
    void push(std::shared_ptr<T> pRes) {
        _items.emplace(_nextId++, pRes);
    }
    // 示例:需要对资源执行的统一修改操作,比如清空所有缓存
    void clear() {
        _items.clear();
        _nextId = 0;
    }
    friend class ResourceManager;
private:
    size_t _nextId{ 0 };
    std::unordered_map<size_t, std::shared_ptr<T>> _items;
};

class ResourceManager{
public:
    ResourceManager() {}
    ~ResourceManager() {
        for (auto& pair : _resources) {
            pair.second.deleter(pair.second.ptr);
        }
    }

    template<typename T>
    void registerResource() {
        Resource<T>* pRes = new Resource<T>;
        const char* typeKey = typeid(T).name();
        ResourceEntry entry;
        entry.ptr = pRes;
        // 注册阶段就把类型转换逻辑封装好,编译期确定类型
        entry.deleter = [](void* rawPtr) {
            delete static_cast<Resource<T>*>(rawPtr);
        };
        entry.clearOp = [](void* rawPtr) {
            static_cast<Resource<T>*>(rawPtr)->clear();
        };
        _resources.emplace(typeKey, entry);
    }

    template<typename T>
    Resource<T>& getResource(){
        // 修正原错误转换逻辑
        return *static_cast<Resource<T>*>(_resources[typeid(T).name()].ptr);
    }

    // 新增:遍历执行所有资源的清空操作
    void clearAll() {
        for (auto& pair : _resources) {
            pair.second.clearOp(pair.second.ptr);
        }
    }

private:
    struct ResourceEntry {
        void* ptr;
        std::function<void(void*)> deleter;
        std::function<void(void*)> clearOp;
        // 后续需要新增其他全局修改操作,只需要在这里加对应function即可
    };
    std::unordered_map<std::string, ResourceEntry> _resources;
};

这种实现的核心逻辑是:类型转换只发生在注册资源的模板实例化阶段,由编译器生成对应类型的转换代码,运行时遍历只需要调用提前存好的函数对象,不需要再做任何运行时类型判断或转换,既安全又没有额外性能开销。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 20:15:39