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

如何无计数增减、无拷贝转换std::shared_ptr<std::unique_ptr<D>>至基类智能指针?

解决std::shared_ptr<std::unique_ptr<D>>转std::shared_ptr<std::unique_ptr<B>>的问题

问题核心在于std::unique_ptr<D>和std::unique_ptr<B>是完全独立的类型(即便D继承自B),两者没有继承关系,因此直接用static_cast转换外层的shared_ptr会失败——std::unique_ptr<D>*无法转换成std::unique_ptr<B>*。

要满足不增减shared_ptr引用计数、不拷贝unique_ptr的要求,可以利用std::shared_ptr的别名构造函数结合reinterpret_cast实现转换:

修改后的可行代码

#include <memory>
#include <cstdio>

struct B {
    virtual void msg() {
        printf("BASE\n");
    }

    virtual ~B() = default;
};

struct D : B {
    D() = default;

    void msg() override {
        printf("DERIVED\n");
    }
};

int main() {
    std::shared_ptr<std::unique_ptr<D>> derivedPtr = std::make_shared<std::unique_ptr<D>>(std::make_unique<D>());
    
    // 先获取内部unique_ptr的原始指针,避免move后原shared_ptr为空
    auto rawUniquePtr = derivedPtr.get();
    // 使用别名构造函数:转移原shared_ptr所有权(引用计数不变),同时转换指针类型
    std::shared_ptr<std::unique_ptr<B>> basePtr(
        std::move(derivedPtr),
        reinterpret_cast<std::unique_ptr<B>*>(rawUniquePtr)
    );
    
    basePtr->get()->msg(); // 输出DERIVED
}

原理说明

  1. 别名构造函数:std::shared_ptr的别名构造函数允许新的shared_ptr与原shared_ptr共享同一个控制块,因此转换过程中引用计数不会增减。通过std::move(derivedPtr)转移原shared_ptr的所有权,原derivedPtr会被置空,控制块的引用计数保持为1。
  2. reinterpret_cast的合理性:对于使用默认删除器的std::unique_ptr,其内存布局仅包含一个指向目标对象的指针,std::unique_ptr<D>和std::unique_ptr<B>的内存结构完全一致,因此reinterpret_cast在GCC、Clang、MSVC等主流编译器下是安全的(虽非标准强制保证,但实际场景中可稳定工作)。

注意事项

如果为std::unique_ptr自定义了删除器,unique_ptr的内存布局可能发生变化,此时reinterpret_cast会导致未定义行为,该方法不再适用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 07:17:40