如何将shared_ptr<void>转换为其他类型?(C++依赖注入场景)
我想实现一个注入类,支持按名称注入任意类,目标是自动把这个注入类传递给被注入类的构造函数——注入类里包含被注入类初始化需要的待管理变量。
当前代码
#include <memory> #include <string> #include <cstdio> // 修正:替换错误的<stdout>为标准头文件<cstdio> #include <typeindex> #include <map> // 补充:使用std::map需包含此头文件 #include <optional> // 补充:使用std::optional需包含此头文件 class Injector { private: std::map<std::type_index, std::map<std::string, std::shared_ptr<void>>> instances; public: template<class T, typename... Args> void inject(std::string name, Args&&... args) { static_assert(std::is_constructible_v<T, Args..., Injector*>, "Cannot construct type with given args"); auto resourcePtr = std::make_shared<T>(std::forward<Args>(args)..., this); instances[typeid(T)][name] = resourcePtr; } template<class T> std::optional<std::shared_ptr<T>> get(std::string name) { // 修正:返回值改为智能指针的可选类型 auto nameToTMap = instances.find(typeid(T)); if (nameToTMap == instances.end()) { return std::nullopt; } auto& mapInstance = nameToTMap->second; // 优化:用引用避免不必要的拷贝 auto instance = mapInstance.find(name); if (instance == mapInstance.end()) { return std::nullopt; } // 核心修复:替换dynamic_pointer_cast为static_pointer_cast auto typedInstance = std::static_pointer_cast<T>(instance->second); return typedInstance; } }; class Foo { public: Foo(int x, int y, Injector* injector) { // 修正:构造函数参数改为指针,匹配inject传递的this指针 printf("Hello from Foo, %i, %i\n", x, y); } }; // 修正:添加类定义结束的分号 int main() { Injector injector; injector.inject<Foo>("foo", 1, 2); // 输出 "Hello from Foo, 1, 2" auto myFoo = injector.get<Foo>("foo"); if (myFoo) { // 可在此处使用获取到的Foo实例 } return 0; }
编译错误
error: 'void' is not a class type _ET* __p = dynamic_cast<_ET*>(__r.get()); ^ ~~~~~~~~~ note: in instantiation of function template specialization 'std::__1::dynamic_pointer_cast<Foo, void>' requested here auto typedInstance = std::dynamic_pointer_cast<T>(instance->second);
疑问
我试过多种方法(比如引入Injectable基类)但都没用,想知道这种实现方式在C++里是否可行?我知道因为void指针是按T类型键存在map里的,所以从shared_ptr
解决方案
1. 核心错误修复
编译错误的根源是**std::dynamic_pointer_cast无法处理void类型**:dynamic_cast要求目标类型是包含虚函数的多态类,且不能对void*执行动态类型转换。但你的实现中,实例是通过typeid(T)作为键存储的,类型完全匹配,因此可以用**std::static_pointer_cast**替代——它是安全的,因为我们能保证存储的shared_ptr<void>本质就是shared_ptr<T>。
2. 其他必要修正
- 返回值类型调整:
get函数原本返回T,但实际需要返回智能指针,因此改为std::optional<std::shared_ptr<T>>,用std::nullopt标识找不到实例的情况。 - 构造函数参数匹配:
Foo的构造函数原本用值传递Injector,但inject函数传递的是this指针,因此改为Injector*类型,避免触发static_assert错误。 - 头文件补充:添加必要的标准库头文件,修正错误的头文件引用。
3. 方案可行性说明
这种实现方式是完全可行的,核心逻辑没有问题:通过std::type_index+名称的双重键结构,将不同类型的实例统一存储,取出时通过static_pointer_cast恢复原类型。只要注入和获取时使用的类型T完全一致,就不会有类型安全问题。
4. 扩展方案:自动解析构造函数依赖
如果需要更强大的自动注入能力(比如自动解析构造函数的所有依赖,而非仅传递Injector指针),可以借助模板元编程或C++20的编译期反射,自动获取构造函数的参数类型并从Injector中提取对应实例。例如借助第三方库Boost.DI可以快速实现这类功能,无需手动编写大量模板代码。
内容的提问来源于stack exchange,提问作者Kyle Johnson

