[英]Convert container of pointers to smart pointers?
Is there a concise, generic way to convert a std
container (such as vector
) of regular/dumb pointers: 是否有一种简洁,通用的方法来转换常规/哑指针的
std
容器(如vector
):
vector< T* >
to, for instance, boost::shared_ptr
?: 举例来说,
boost::shared_ptr
vector< boost::shared_ptr<T> >
I thought I could pull it off using vector
's range constructor: 我以为我可以使用
vector
的范围构造函数将其拉出来:
vector< T* > vec_a;
...
vector< boost::shared_ptr<T> > vec_b( vec_a.begin(), vec_a.end() );
but that refused to compile (Visual Studio 2008). 但那拒绝编译(Visual Studio 2008)。
EDIT: Test code: 编辑:测试代码:
void test()
{
vector< int* > vec_a;
vector< boost::shared_ptr<int> > vec_b( vec_a.begin(), vec_a.end() );
}
Compilation errors: 编译错误:
1>c:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\include\memory(131) : error C2664: 'std::allocator<_Ty>::construct' : cannot convert parameter 2 from 'int *' to 'const boost::shared_ptr<T> &'
1> with
1> [
1> _Ty=boost::shared_ptr<int>
1> ]
1> and
1> [
1> T=int
1> ]
1> Reason: cannot convert from 'int *' to 'const boost::shared_ptr<T>'
1> with
1> [
1> T=int
1> ]
1> Constructor for class 'boost::shared_ptr<T>' is declared 'explicit'
1> with
1> [
1> T=int
1> ]
1> c:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\include\memory(822) : see reference to function template instantiation '_FwdIt std::_Uninit_copy<int**,_FwdIt,_Alloc>(_InIt,_InIt,_FwdIt,_Alloc &,std::_Nonscalar_ptr_iterator_tag,std::_Range_checked_iterator_tag)' being compiled
1> with
1> [
1> _FwdIt=boost::shared_ptr<int> *,
1> _Alloc=std::allocator<boost::shared_ptr<int>>,
1> _InIt=int **
1> ]
1> c:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\include\vector(1141) : see reference to function template instantiation '_FwdIt stdext::unchecked_uninitialized_copy<_Iter,boost::shared_ptr<T>*,std::allocator<_Ty>>(_InIt,_InIt,_FwdIt,_Alloc &)' being compiled
1> with
1> [
1> _FwdIt=boost::shared_ptr<int> *,
1> _Iter=std::_Vector_iterator<int *,std::allocator<int *>>,
1> T=int,
1> _Ty=boost::shared_ptr<int>,
1> _InIt=std::_Vector_iterator<int *,std::allocator<int *>>,
1> _Alloc=std::allocator<boost::shared_ptr<int>>
1> ]
1> c:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\include\vector(956) : see reference to function template instantiation 'boost::shared_ptr<T> *std::vector<_Ty>::_Ucopy<_Iter>(_Iter,_Iter,boost::shared_ptr<T> *)' being compiled
1> with
1> [
1> T=int,
1> _Ty=boost::shared_ptr<int>,
1> _Iter=std::_Vector_iterator<int *,std::allocator<int *>>
1> ]
1> c:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\include\vector(889) : see reference to function template instantiation 'void std::vector<_Ty>::_Insert<_Iter>(std::_Vector_const_iterator<_Ty,_Alloc>,_Iter,_Iter,std::forward_iterator_tag)' being compiled
1> with
1> [
1> _Ty=boost::shared_ptr<int>,
1> _Iter=std::_Vector_iterator<int *,std::allocator<int *>>,
1> _Alloc=std::allocator<boost::shared_ptr<int>>
1> ]
1> c:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\include\vector(537) : see reference to function template instantiation 'void std::vector<_Ty>::insert<_Iter>(std::_Vector_const_iterator<_Ty,_Alloc>,_Iter,_Iter)' being compiled
1> with
1> [
1> _Ty=boost::shared_ptr<int>,
1> _Iter=std::_Vector_iterator<int *,std::allocator<int *>>,
1> _Alloc=std::allocator<boost::shared_ptr<int>>
1> ]
1> c:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\include\vector(514) : see reference to function template instantiation 'void std::vector<_Ty>::_Construct<_Iter>(_Iter,_Iter,std::input_iterator_tag)' being compiled
1> with
1> [
1> _Ty=boost::shared_ptr<int>,
1> _Iter=std::_Vector_iterator<int *,std::allocator<int *>>
1> ]
1> .\test.cpp(8364) : see reference to function template instantiation 'std::vector<_Ty>::vector<std::_Vector_iterator<int,_Alloc>>(_Iter,_Iter)' being compiled
1> with
1> [
1> _Ty=boost::shared_ptr<int>,
1> _Alloc=std::allocator<int *>,
1> _Iter=std::_Vector_iterator<int *,std::allocator<int *>>
1> ]
You could use std::transform
: 你可以使用
std::transform
:
template <typename T>
boost::shared_ptr<T> to_shared_ptr(T * p) { return boost::shared_ptr<T>(p); }
vec_b.resize(vec_a.size());
std::transform(vec_a.begin(), vec_a.ebd(), vec_b.begin(), to_shared_ptr);
However , the recommended practice is to assign raw pointers to smart pointers immediately after creation. 但是 ,建议的做法是在创建后立即为智能指针分配原始指针。 Having the raw pointers put in a container, then copying them to another another container looks dangerous.
将原始指针放入容器中,然后将它们复制到另一个容器看起来很危险。 YOu need to make sure noone else ever frees these raw pointers.
你需要确保没有其他人能够释放这些原始指针。 You could emphasize that by
vec_a.clear()
immediately after the transfer - but that's far from a guarantee. 您可以在转移后立即通过
vec_a.clear()
强调这vec_a.clear()
- 但这远非保证。
Combine ::std::back_inserter
with ::std::transform
and a little function that will perform the conversion. 将
::std::back_inserter
与::std::transform
以及一个将执行转换的小函数。 If you also use reserve
, this should be reasonably efficient. 如果你也使用
reserve
,这应该是相当有效的。 Once all the templates are expanded you will essentially get this code: 一旦扩展了所有模板,您将基本上获得以下代码:
template <class T>
static inline ::std::tr1::shared_ptr<T> to_shared_ptr(T *val)
{
return ::std::tr1::shared_ptr<T>(val);
}
void test()
{
::std::vector< int* > vec_a;
::std::vector< ::std::tr1::shared_ptr<int> > vec_b;
vec_b.reserve(vec_a.size());
::std::transform(vec_a.begin(), vec_a.end(), ::std::back_inserter(vec_b),
to_shared_ptr<int>);
vec_a.clear();
}
According to the documentation of Boost shared_ptr
, the shared_ptr
constructor is marked explicit
, meaning that there's no implicit conversion from T*
s to shared_ptr<T>
. 根据Boost
shared_ptr
的文档 , shared_ptr
构造函数被标记为explicit
,这意味着没有从T*
s到shared_ptr<T>
隐式转换。 Consequently, when you're trying to insert the iterator range defining your old container into the new container, the compiler complains because there's no way of implicitly converting from the raw pointers of the old containers into the shared_ptr
s of the new container. 因此,当您尝试将定义旧容器的迭代器范围插入到新容器中时,编译器会抱怨,因为无法从旧容器的原始指针隐式转换为新容器的
shared_ptr
。 You can fix this by either using a back_inserter
and transform
, or by just doing the iteration by hand to wrap each pointer and insert it one at a time. 您可以通过使用
back_inserter
和transform
,或者通过手动迭代来包装每个指针并一次插入一个来解决此问题。
It turns out that there's a good reason that you don't want this conversion to work implicitly. 事实证明,你有一个很好的理由不希望这种转换隐含地发挥作用。 Consider:
考虑:
void DoSomething(shared_ptr<T> ptr) {
/* ... */
}
T* ptr = new T();
DoSomething(ptr);
If the implicit conversion were allowed here, then the call to DoSomething
would be legal. 如果允许隐式转换,则调用
DoSomething
将是合法的。 However, this would cause a new shared_ptr
to start referencing the resource held by ptr
. 但是,这会导致新的
shared_ptr
开始引用ptr
所持有的资源。 This is problematic, because when this shared_ptr
goes out of scope when DoSomething
returns, it will see that it's the only shared_ptr
to the resource and will deallocate it. 这是有问题的,因为当
DoSomething
返回时,当shared_ptr
超出范围时,它将看到它是资源的唯一shared_ptr
并将解除分配它。 In other words, calling DoSomething
with a raw pointer would implicitly delete the pointer! 换句话说,使用原始指针调用
DoSomething
将隐式删除指针!
Just because you said for instance, boost::shared_ptr
- if the semantics fit you, there is boost::ptr_vector
which stores a vector of pointers and is responsible for freeing them when the vector goes out of scope. 正因为你所说的
for instance, boost::shared_ptr
- 如果语义适合你,有boost::ptr_vector
存储一个指针向量,并负责在向量超出范围时释放它们。 This container has a transfer
method that you could use for taking ownership of the pointers cointained in the vector you get. 这个容器有一个
transfer
方法,你可以使用它来获取你得到的向量中包含的指针的所有权。
Just use the range constructor like so: 只需使用范围构造函数,如下所示:
vector<int*> nums = { new int(1), new int(5), new int(10) };
vector<shared_ptr<int>> smart_nums(nums.begin(), nums.end());
Conceptually it is equivalent to: 从概念上讲,它相当于:
for (int num : nums)
smart_nums.emplace_back(num);
Now with the range constructor, the following is possible: 现在使用范围构造函数,可以实现以下功能:
class Num_container {
public:
Num_container(vector<int*> nums)
: smart_nums(nums.begin(), nums.end()) { }
private:
vector<shared_ptr<int>> smart_nums;
};
This makes dealing with containers of polymorphic types much easier! 这使得处理多态类型的容器变得更加容易!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.