如何无计数增减、无拷贝转换std::shared_ptr<std::unique_ptr<D>>至基类智能指针?
问题核心在于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 }
原理说明
- 别名构造函数:
std::shared_ptr的别名构造函数允许新的shared_ptr与原shared_ptr共享同一个控制块,因此转换过程中引用计数不会增减。通过std::move(derivedPtr)转移原shared_ptr的所有权,原derivedPtr会被置空,控制块的引用计数保持为1。 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
相关产品推荐
相关产品推荐

