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

如何将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转成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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 16:08:01