简体   繁体   English

将指针容器转换为智能指针?

[英]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_insertertransform ,或者通过手动迭代来包装每个指针并一次插入一个来解决此问题。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM