【问题标题】:Convert container of pointers to smart pointers?将指针容器转换为智能指针?
【发布时间】:2011-06-14 19:44:13
【问题描述】:

是否有一种简洁、通用的方法来转换常规/哑指针的std 容器(例如vector):

vector< T* >

例如,boost::shared_ptr?:

vector< boost::shared_ptr<T> >

我想我可以使用vector 的范围构造函数来完成它:

vector< T* > vec_a;
...
vector< boost::shared_ptr<T> > vec_b( vec_a.begin(), vec_a.end() );

但那拒绝编译 (Visual Studio 2008)。

编辑:测试代码:

void test()
{
vector< int* > vec_a;
vector< boost::shared_ptr<int> > vec_b( vec_a.begin(), vec_a.end() );
}

编译错误:

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>        ]

【问题讨论】:

  • 你在 VS2008 中遇到了什么错误?
  • @templatetypedef: boost::shared_ptr 不允许从原始指针隐式构造(因为这可能会隐藏语义的变化),因此编译器会认为类型不相关。
  • @templatetypedef:添加了我的测试存根和错误
  • 如果可以,请考虑使用::std::tr1::shared_ptr。避免对第三方库的依赖是很好的,即使是像 boost 这样壮观的库。

标签: c++ pointers boost stl smart-pointers


【解决方案1】:

你可以使用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);

然而,推荐的做法是在创建后立即将原始指针分配给智能指针。将原始指针放入容器中,然后将它们复制到另一个容器中看起来很危险。你需要确保没有其他人释放过这些原始指针。您可以在转移后立即通过vec_a.clear() 强调这一点——但这远非保证。

【讨论】:

  • 我正在使用一个遗留 API,它希望附加到通过引用传入的 vector&lt;T*&gt;。我希望让我的使用更安全。
  • 您还需要确保在智能指针释放内存后没有人使用原始指针,只是为了增加乐趣。
  • @genpfault:我认为你有正当理由。如果旧版 API 说“你必须在使用完它们后删除它们,我再也不会看它们了”,你应该没问题。
  • @peterchen:是的,这些正是我正在使用的语义。
  • 如果你想使用vec_b.begin()作为输出迭代器,那么你需要先在vec_b中腾出空间,通过构造函数或者resize()而不是reserve()。如果你只想reserve()(或什么都不做),那么你需要back_inserter(vec_b)作为输出迭代器。
【解决方案2】:

::std::back_inserter::std::transform 和一个执行转换的小函数结合起来。如果您还使用reserve,这应该是相当有效的。展开所有模板后,您基本上会得到以下代码:

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();
}

【讨论】:

  • ITYM return ::std::tr1::shared_ptr&lt;T&gt;(val);
【解决方案3】:

根据the documentation of Boost shared_ptrshared_ptr 构造函数标记为explicit,这意味着没有从T*s 到shared_ptr&lt;T&gt; 的隐式转换。因此,当您尝试将定义旧容器的迭代器范围插入新容器时,编译器会报错,因为无法将旧容器的原始指针隐式转换为新容器的shared_ptrs。您可以通过使用back_insertertransform 来解决此问题,或者只需手动进行迭代以包装每个指针并一次插入一个。

事实证明,您不希望这种转换隐式进行是有充分理由的。考虑:

void DoSomething(shared_ptr<T> ptr) {
     /* ... */
}

T* ptr = new T();
DoSomething(ptr);

如果此处允许隐式转换,则对DoSomething 的调用将是合法的。但是,这会导致新的shared_ptr 开始引用ptr 持有的资源。这是有问题的,因为当DoSomething 返回时,当这个shared_ptr 超出范围时,它将看到它是资源的唯一shared_ptr 并将释放它。换句话说,使用原始指针调用DoSomething 会隐式删除指针!

【讨论】:

    【解决方案4】:

    就因为你说for instance, boost::shared_ptr - 如果语义适合你,有boost::ptr_vector 存储一个指针向量,并在向量超出范围时负责释放它们。这个容器有一个transfer 方法,您可以使用它来获取您获得的向量中包含的指针的所有权。

    【讨论】:

      【解决方案5】:

      只需像这样使用范围构造函数:

      vector<int*> nums = { new int(1), new int(5), new int(10) };
      vector<shared_ptr<int>> smart_nums(nums.begin(), nums.end());
      

      在概念上相当于:

      for (int num : nums)
          smart_nums.emplace_back(num);
      

      现在使用范围构造函数,以下是可能的:

      class Num_container {
      public:
          Num_container(vector<int*> nums)
              : smart_nums(nums.begin(), nums.end()) { }
      private:
          vector<shared_ptr<int>> smart_nums;
      };
      

      这使得处理多态类型的容器变得更加容易!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-09-21
        • 2014-03-04
        • 1970-01-01
        • 2022-01-23
        • 1970-01-01
        • 2016-12-08
        • 1970-01-01
        • 2012-10-24
        相关资源
        最近更新 更多