Преобразовать контейнер указателей в умные указатели? - PullRequest
3 голосов
/ 25 января 2011

Существует ли краткий, общий способ преобразования 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>        ]

Ответы [ 5 ]

9 голосов
/ 25 января 2011

Вы можете использовать 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() сразу после перевода - но это далеко не гарантия.

7 голосов
/ 25 января 2011

Согласно документации Boost shared_ptr, конструктор shared_ptr помечен explicit, что означает, что не существует неявного преобразования из T* s в shared_ptr<T>.Следовательно, когда вы пытаетесь вставить диапазон итераторов, определяющих ваш старый контейнер, в новый контейнер, компилятор жалуется, потому что нет способа неявного преобразования из необработанных указателей старых контейнеров в shared_ptr s нового контейнера.Вы можете исправить это, используя back_inserter и transform, или просто выполнив итерацию вручную, чтобы обернуть каждый указатель и вставить его по одному.

Оказывается, что есть веская причиначто вы не хотите, чтобы это преобразование работало неявно.Рассмотрим:

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

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

Если бы здесь было разрешено неявное преобразование, то вызов DoSomething был бы законным.Однако это приведет к тому, что новый shared_ptr начнет ссылаться на ресурс, который хранится в ptr.Это проблематично, потому что когда этот shared_ptr выходит из области видимости, когда возвращается DoSomething, он увидит, что это единственный shared_ptr ресурс, и освободит его.Другими словами, вызов DoSomething с необработанным указателем приведет к неявному удалению указателя!

7 голосов
/ 25 января 2011

Объедините ::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();
}
3 голосов
/ 25 января 2011

Только потому, что вы сказали for instance, boost::shared_ptr - если семантика вам подходит, есть boost::ptr_vector, которая хранит вектор указателей и отвечает за освобождение их, когда вектор выходит из области видимости. В этом контейнере есть метод transfer, который вы можете использовать для получения прав владения указателями, содержащимися в векторе, который вы получаете.

1 голос
/ 18 июля 2015

Просто используйте конструктор диапазона следующим образом:

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

Это значительно облегчает работу с контейнерами полиморфного типа!

...