当我尝试深度复制 `unique_ptr` 时出现段错误
getting a segfault when I try to deep copy `unique_ptr`s
为什么我不能 return 来自 clone
函数的 unique_ptr
?我以为 I could do this.
我有一个名为 transform
的不同数学函数的基础 class。我有一个指向这种类型的指针容器,因为我使用的是多态性。例如,所有这些派生的 classes 都有不同的 log_jacobian
实现,这对统计算法很有用。
我正在对这个 transform
class 使用 unique_ptr
s,所以我制作了一个(纯虚拟的)clone
函数来创建新的 unique_ptr
指向同一数学 transform
对象的深层副本。这个新对象与派生自 transform<float_t>
的类型相同,但它是一个独立的对象,因为您不能让两个 unique_ptr
指向同一事物。
template<typename float_t>
class transform{
...
virtual std::unique_ptr<transform<float_t>> clone() const = 0;
...
};
我的 transform_container
class 一次拥有其中的一些。毕竟,大多数统计模型都有不止一个参数。
template<typename float_t, size_t numelem>
class transform_container{
private:
using array_ptrs = std::array<std::unique_ptr<transform<float_t>>, numelem>;
array_ptrs m_ts;
unsigned m_add_idx;
...
auto get_transforms() const -> array_ptrs;
};
不过,我不确定为什么深度复制功能 get_transforms
不起作用。它用于制作副本,并从容器中访问单独的转换。当我 运行 一些测试时,我遇到了段错误。如果我 运行 它在 gdb
中,它明确地告诉我该行在它之后带有注释。
template<typename float_t, size_t numelem>
auto transform_container<float_t,numelem>::get_transforms() const -> array_ptrs
{
array_ptrs deep_cpy;
for(size_t i = 0; i < numelem; ++i){
deep_cpy[i] = m_ts[i]->clone(); // this line
}
return deep_cpy;
}
我也试过 std::move
将其插入 deep_cpy[i]
并使用 unique_ptr::reset
,但无济于事。
编辑:
这里有一些其他相关的方法:一个向 transform_container
添加转换的方法,以及个人 transform
:
的工厂方法
template<typename float_t>
std::unique_ptr<transform<float_t> > transform<float_t>::create(trans_type tt)
{
if(tt == trans_type::TT_null){
return std::unique_ptr<transform<float_t> >(new null_trans<float_t> );
}else if(tt == trans_type::TT_twice_fisher){
return std::unique_ptr<transform<float_t> >(new twice_fisher_trans<float_t> );
}else if(tt == trans_type::TT_logit){
return std::unique_ptr<transform<float_t> >(new logit_trans<float_t> );
}else if(tt == trans_type::TT_log){
return std::unique_ptr<transform<float_t> >(new log_trans<float_t> );
}else{
throw std::invalid_argument("that transform type was not accounted for");
}
}
template<typename float_t, size_t numelem>
void transform_container<float_t, numelem>::add_transform(trans_type tt)
{
m_ts[m_add_idx] = transform<float_t>::create(tt);
m_add_idx++;
}
在 get_transforms()
中,您正在遍历 整个 m_ts[]
数组,在 all[= 上调用 clone()
45=] 个元素 - 甚至 add_transform()
尚未分配的元素!未分配的 unique_ptr
s 将持有一个 nullptr
指针,它是 undefined behavior to call a non-static class method through a nullptr
.
最简单的解决方法是将 get_transforms()
中的循环更改为使用 m_add_idx
而不是 numelem
:
template<typename float_t, size_t numelem>
auto transform_container<float_t,numelem>::get_transforms() const -> array_ptrs
{
array_ptrs deep_cpy;
for(size_t i = 0; i < m_add_idx; ++i){ // <-- here
deep_cpy[i] = m_ts[i]->clone();
}
return deep_cpy;
}
否则,您将不得不手动忽略任何 nullptr
元素,例如:
template<typename float_t, size_t numelem>
auto transform_container<float_t,numelem>::get_transforms() const -> array_ptrs
{
array_ptrs deep_cpy;
for(size_t i = 0, j = 0; i < numelem; ++i){
if (m_ts[i]) {
deep_cpy[j++] = m_ts[i]->clone();
}
}
return deep_cpy;
}
无论哪种方式,您都应该更新 add_transform()
以验证 m_add_idx
不会超过 numelem
:
template<typename float_t, size_t numelem>
void transform_container<float_t, numelem>::add_transform(trans_type tt)
{
if (m_add_idx >= numelem) throw std::length_error("cant add any more transforms"); // <-- here
m_ts[m_add_idx] = transform<float_t>::create(tt);
++m_add_idx;
}
也就是说,由于 transform_container
可以分配可变数量的转换,我建议更改 transform_container
以使用 std::vector
而不是 std::array
,例如:
template<typename float_t>
class transform_container{
private:
using vector_ptrs = std::vector<std::unique_ptr<transform<float_t>>>;
vector_ptrs m_ts;
...
auto get_transforms() const -> vector_ptrs;
};
template<typename float_t>
auto transform_container<float_t>::get_transforms() const -> vector_ptrs
{
vector_ptrs deep_cpy;
deep_cpy.reserve(m_ts.size());
for(const auto &elem : m_ts){
deep_cpy.push_back(elem->clone());
}
return deep_cpy;
}
template<typename float_t>
std::unique_ptr<transform<float_t>> transform<float_t>::create(trans_type tt)
{
switch (tt) {
case trans_type::TT_null:
return std::make_unique<null_trans<float_t>>();
case trans_type::TT_twice_fisher:
return std::make_unique<twice_fisher_trans<float_t>>();
case trans_type::TT_logit:
return std::make_unique<logit_trans<float_t>>();
case trans_type::TT_log:
return std::make_unique<log_trans<float_t>>();
}
throw std::invalid_argument("that transform type was not accounted for");
}
template<typename float_t>
void transform_container<float_t>::add_transform(trans_type tt)
{
m_ts.push_back(transform<float_t>::create(tt));
}
为什么我不能 return 来自 clone
函数的 unique_ptr
?我以为 I could do this.
我有一个名为 transform
的不同数学函数的基础 class。我有一个指向这种类型的指针容器,因为我使用的是多态性。例如,所有这些派生的 classes 都有不同的 log_jacobian
实现,这对统计算法很有用。
我正在对这个 transform
class 使用 unique_ptr
s,所以我制作了一个(纯虚拟的)clone
函数来创建新的 unique_ptr
指向同一数学 transform
对象的深层副本。这个新对象与派生自 transform<float_t>
的类型相同,但它是一个独立的对象,因为您不能让两个 unique_ptr
指向同一事物。
template<typename float_t>
class transform{
...
virtual std::unique_ptr<transform<float_t>> clone() const = 0;
...
};
我的 transform_container
class 一次拥有其中的一些。毕竟,大多数统计模型都有不止一个参数。
template<typename float_t, size_t numelem>
class transform_container{
private:
using array_ptrs = std::array<std::unique_ptr<transform<float_t>>, numelem>;
array_ptrs m_ts;
unsigned m_add_idx;
...
auto get_transforms() const -> array_ptrs;
};
不过,我不确定为什么深度复制功能 get_transforms
不起作用。它用于制作副本,并从容器中访问单独的转换。当我 运行 一些测试时,我遇到了段错误。如果我 运行 它在 gdb
中,它明确地告诉我该行在它之后带有注释。
template<typename float_t, size_t numelem>
auto transform_container<float_t,numelem>::get_transforms() const -> array_ptrs
{
array_ptrs deep_cpy;
for(size_t i = 0; i < numelem; ++i){
deep_cpy[i] = m_ts[i]->clone(); // this line
}
return deep_cpy;
}
我也试过 std::move
将其插入 deep_cpy[i]
并使用 unique_ptr::reset
,但无济于事。
编辑:
这里有一些其他相关的方法:一个向 transform_container
添加转换的方法,以及个人 transform
:
template<typename float_t>
std::unique_ptr<transform<float_t> > transform<float_t>::create(trans_type tt)
{
if(tt == trans_type::TT_null){
return std::unique_ptr<transform<float_t> >(new null_trans<float_t> );
}else if(tt == trans_type::TT_twice_fisher){
return std::unique_ptr<transform<float_t> >(new twice_fisher_trans<float_t> );
}else if(tt == trans_type::TT_logit){
return std::unique_ptr<transform<float_t> >(new logit_trans<float_t> );
}else if(tt == trans_type::TT_log){
return std::unique_ptr<transform<float_t> >(new log_trans<float_t> );
}else{
throw std::invalid_argument("that transform type was not accounted for");
}
}
template<typename float_t, size_t numelem>
void transform_container<float_t, numelem>::add_transform(trans_type tt)
{
m_ts[m_add_idx] = transform<float_t>::create(tt);
m_add_idx++;
}
在 get_transforms()
中,您正在遍历 整个 m_ts[]
数组,在 all[= 上调用 clone()
45=] 个元素 - 甚至 add_transform()
尚未分配的元素!未分配的 unique_ptr
s 将持有一个 nullptr
指针,它是 undefined behavior to call a non-static class method through a nullptr
.
最简单的解决方法是将 get_transforms()
中的循环更改为使用 m_add_idx
而不是 numelem
:
template<typename float_t, size_t numelem>
auto transform_container<float_t,numelem>::get_transforms() const -> array_ptrs
{
array_ptrs deep_cpy;
for(size_t i = 0; i < m_add_idx; ++i){ // <-- here
deep_cpy[i] = m_ts[i]->clone();
}
return deep_cpy;
}
否则,您将不得不手动忽略任何 nullptr
元素,例如:
template<typename float_t, size_t numelem>
auto transform_container<float_t,numelem>::get_transforms() const -> array_ptrs
{
array_ptrs deep_cpy;
for(size_t i = 0, j = 0; i < numelem; ++i){
if (m_ts[i]) {
deep_cpy[j++] = m_ts[i]->clone();
}
}
return deep_cpy;
}
无论哪种方式,您都应该更新 add_transform()
以验证 m_add_idx
不会超过 numelem
:
template<typename float_t, size_t numelem>
void transform_container<float_t, numelem>::add_transform(trans_type tt)
{
if (m_add_idx >= numelem) throw std::length_error("cant add any more transforms"); // <-- here
m_ts[m_add_idx] = transform<float_t>::create(tt);
++m_add_idx;
}
也就是说,由于 transform_container
可以分配可变数量的转换,我建议更改 transform_container
以使用 std::vector
而不是 std::array
,例如:
template<typename float_t>
class transform_container{
private:
using vector_ptrs = std::vector<std::unique_ptr<transform<float_t>>>;
vector_ptrs m_ts;
...
auto get_transforms() const -> vector_ptrs;
};
template<typename float_t>
auto transform_container<float_t>::get_transforms() const -> vector_ptrs
{
vector_ptrs deep_cpy;
deep_cpy.reserve(m_ts.size());
for(const auto &elem : m_ts){
deep_cpy.push_back(elem->clone());
}
return deep_cpy;
}
template<typename float_t>
std::unique_ptr<transform<float_t>> transform<float_t>::create(trans_type tt)
{
switch (tt) {
case trans_type::TT_null:
return std::make_unique<null_trans<float_t>>();
case trans_type::TT_twice_fisher:
return std::make_unique<twice_fisher_trans<float_t>>();
case trans_type::TT_logit:
return std::make_unique<logit_trans<float_t>>();
case trans_type::TT_log:
return std::make_unique<log_trans<float_t>>();
}
throw std::invalid_argument("that transform type was not accounted for");
}
template<typename float_t>
void transform_container<float_t>::add_transform(trans_type tt)
{
m_ts.push_back(transform<float_t>::create(tt));
}