是否可以为另一个对象重用智能指针的内存?
Is it possible to reuse a smart pointer's memory for another object?
假设我有两个大小完全相同的不相关类型。
struct Foo { ... };
struct Bar { ... };
现在,假设我有一棵 Foo
的树,我想将其转换为 Bar
.
的树
template <typename Leaf>
struct Tree
{
Leaf data;
std::unique_ptr<Tree> lhs;
std::unique_ptr<Tree> rhs;
};
Tree<Foo> footree = ...;
Tree<Bar> bartree = convert(std::move(footree)); // <--
如何在重用 footree
的堆分配内存的同时执行此转换?换句话说,我如何(安全地)将 Tree<Foo>
转换为 Tree<Bar>
而不分配额外的内存?
这是我实际尝试做的一个简化示例。实际上 Foo
和 Bar
是递归变体,具有不同数量的替代类型。
对于原始指针,这并不困难。只需手动触发 Foo
的析构函数并通过放置 new 手动应用构造函数。 enable_if
是确保大小和对齐匹配的 SFINAE。
#include <iostream>
#include <vector>
template<typename U, typename V, typename... Args,
std::enable_if_t<sizeof(U) == sizeof(V) && alignof(U) == alignof(V), int> = 0>
U* repurpose_raw_ptr(V* ptr, Args&&... args)
{
ptr->~V();
return new ((void*)ptr) U(std::forward<Args>(args)...);
}
int main()
{
std::vector<int>* X = new std::vector<int>({1,2,3,4,5});
std::vector<double>* Y =
repurpose_raw_ptr<std::vector<double>>(X,
std::initializer_list<double>{1.,2.,3.,4.,5.});
std::cout << Y->at(0) << " " << Y->at(4);
delete Y;
return 0;
}
您可以应用函数从 std::unique_ptr<Foo>
“转换”为 std::unique_ptr<Bar>
,方法是使用释放对象所有权的唯一指针的 release()
方法。类似于:
std::unique_ptr<Bar> X = ...;
std::unique_ptr<Foo> Y = std::unique_ptr<Foo>(repurpose_raw_ptr<Bar>(X.release(),...));
这里假设 delete Foo
和 delete Bar
与 Foo/Bar 的销毁 + 原始指针的释放相同,我相信情况可能并非总是如此。
一般来说,我不建议这样做,因为在跨平台环境中很难确保结构具有相同的size/alignment。
编辑:以前,错过了您想以某种方式将 Foo
转换为 Bar
。您必须在别处临时 copy/move 实例 Foo
然后触发内存重新调整用途。
假设我有两个大小完全相同的不相关类型。
struct Foo { ... };
struct Bar { ... };
现在,假设我有一棵 Foo
的树,我想将其转换为 Bar
.
template <typename Leaf>
struct Tree
{
Leaf data;
std::unique_ptr<Tree> lhs;
std::unique_ptr<Tree> rhs;
};
Tree<Foo> footree = ...;
Tree<Bar> bartree = convert(std::move(footree)); // <--
如何在重用 footree
的堆分配内存的同时执行此转换?换句话说,我如何(安全地)将 Tree<Foo>
转换为 Tree<Bar>
而不分配额外的内存?
这是我实际尝试做的一个简化示例。实际上 Foo
和 Bar
是递归变体,具有不同数量的替代类型。
对于原始指针,这并不困难。只需手动触发 Foo
的析构函数并通过放置 new 手动应用构造函数。 enable_if
是确保大小和对齐匹配的 SFINAE。
#include <iostream>
#include <vector>
template<typename U, typename V, typename... Args,
std::enable_if_t<sizeof(U) == sizeof(V) && alignof(U) == alignof(V), int> = 0>
U* repurpose_raw_ptr(V* ptr, Args&&... args)
{
ptr->~V();
return new ((void*)ptr) U(std::forward<Args>(args)...);
}
int main()
{
std::vector<int>* X = new std::vector<int>({1,2,3,4,5});
std::vector<double>* Y =
repurpose_raw_ptr<std::vector<double>>(X,
std::initializer_list<double>{1.,2.,3.,4.,5.});
std::cout << Y->at(0) << " " << Y->at(4);
delete Y;
return 0;
}
您可以应用函数从 std::unique_ptr<Foo>
“转换”为 std::unique_ptr<Bar>
,方法是使用释放对象所有权的唯一指针的 release()
方法。类似于:
std::unique_ptr<Bar> X = ...;
std::unique_ptr<Foo> Y = std::unique_ptr<Foo>(repurpose_raw_ptr<Bar>(X.release(),...));
这里假设 delete Foo
和 delete Bar
与 Foo/Bar 的销毁 + 原始指针的释放相同,我相信情况可能并非总是如此。
一般来说,我不建议这样做,因为在跨平台环境中很难确保结构具有相同的size/alignment。
编辑:以前,错过了您想以某种方式将 Foo
转换为 Bar
。您必须在别处临时 copy/move 实例 Foo
然后触发内存重新调整用途。