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++是静态类型语言,所有指针类型转换必须在编译阶段明确目标类型,运行时不存在“根据字符串自动匹配类型做转换”的机制。
你当前的实现还有两个显性问题,需要先修正:
register是C++关键字,不能作为函数名,会直接编译失败。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
相关产品推荐
相关产品推荐

