Подтвердить что ты не робот

Вектор unique_ptr в С++ 11

Недавно я перешел на С++ 11, и я стараюсь привыкнуть к хорошим практикам. То, что я в конечном итоге занимаюсь очень часто, это что-то вроде:

class Owner
{
private:
    vector<unique_ptr<HeavyResource>> _vectorOfHeavyResources;
public:
    virtual const vector<const HeavyResource*>* GetVectorOfResources() const;
};

Это требует от меня сделать что-то вроде добавления _returnableVector и перевода исходных векторов, чтобы иметь возможность вернуть его позже:

_returnableVector = vector<HeavyResource*>;
for (int i=0; i< _vectorOfHeavyResources.size(); i++)
{
    _returnableVector.push_back(_vectorOfHeavyResources[i].get());
}

Кто-нибудь заметил подобную проблему? Каковы ваши мысли и решения? Я получаю всю идею собственности прямо здесь?

UPDATE: Есть еще одна вещь: Что делать, если один класс возвращает результат некоторой обработки как vector<unique_ptr<HeavyResource>> (он передает право собственности на результаты вызывающему абоненту), и предполагается, что он будет использоваться для последующей обработки:

vector<unique_ptr<HeavyResource>> partialResult = _processor1.Process();
// translation
auto result = _processor2.Process(translatedPartialResult); // the argument of process is vector<const HeavyResource*>
4b9b3361

Ответ 1

Я бы предположил, что вместо сохранения и возврата вектора un unique_ptr ed вы предоставляете функции для прямого доступа к элементам. Это инкапсулирует хранение ваших ресурсов; клиенты не знают, что они хранятся как unique_ptr s, и что они хранятся в vector.

Одной из возможностей для этого является использование boost::indirect_iterator для автоматического разыгрывания вашего unique_ptr:

using ResourceIterator =
     boost::indirect_iterator<std::vector<std::unique_ptr<HeavyResource>>::iterator,
                              const HeavyResource>;
ResourceIterator begin() { return std::begin(_vectorOfHeavyResources); }
ResourceIterator end() { return std::end(_vectorOfHeavyResources); }

Демо

Ответ 2

Если вы столкнулись с этим часто, может возникнуть смысл написать класс, который ведет себя как unique_ptr, но передает константу указателя на объект, на который он указывает. Таким образом, вы можете просто вернуть ссылку const на ваш вектор.

В итоге я написал это один раз и сделаю с ним:

#include <iostream>
#include <vector>
#include <memory>

//unique,const-preserving pointer
template<class T>
class ucp_ptr {
    std::unique_ptr<T> ptr;
public:
    ucp_ptr() = default;
    ucp_ptr(T* ptr) :ptr{ ptr }{};
    ucp_ptr(std::unique_ptr<T>&& other) :ptr(std::move(other)){};

    T&        operator*()       { return ptr.get(); }
    T const & operator*()const  { return ptr.get(); }

    T*        operator->()      { return ptr.get(); }
    T const * operator->()const { return ptr.get(); }
};

struct Foo {
    int a = 0;
};

int main() {
    std::vector<ucp_ptr<Foo>> v;
    v.emplace_back(new Foo());
    v.emplace_back(std::make_unique<Foo>());    

    v[0]->a = 1;
    v[1]->a = 2;

    const std::vector<ucp_ptr<Foo>>& cv = v;

    std::cout << cv[0]->a << std::endl; //<-read access OK
    //cv[1]->a = 10; //<-compiler error
}

Конечно, вы можете немного расширить его, если вам нужны пользовательские удалители или хотите добавить специализацию для управления массивами, но это базовая версия. Я также верю, что я видел более утонченную версию этого somwhere здесь, на SO, но я не могу найти его прямо сейчас.

Вот пример того, как это можно использовать в классе:

class Bar {
    std::vector<ucp_ptr<Foo>> v;
public:
    void add(const Foo& foo){ 
        v.push_back(std::make_unique<Foo>(foo)); 
    }
    //modifying elements
    void doubleElements() {
        for (auto& e : v){
            e->a *= 2;
        }
    }
    const std::vector<ucp_ptr<Foo>>& showElements() const{
        return v;
    }
};

ИЗМЕНИТЬ

Что касается вашего обновления, вы должны жить с тем фактом, что vector<T> не имеет отношения к vector<B>, даже если было бы правильно отбрасывать T в B и наоборот.
Вы можете писать адаптеры, которые дают вам другое представление об элементах (путем приведения каждого элемента, когда это необходимо), но - помимо создания нового вектора правильного типа - не существует общего механизма (что я знаю), чтобы делать то, что вы хотите.

Ответ 3

Вместо этого вы можете рассмотреть shared_ptr, поскольку он, вероятно, отражает то, что вы пытаетесь достичь. Вероятно, вы захотите использовать unique_ptr в меньших масштабах или в любое время, когда хотите отразить только одну вещь, использующую объект.

Ответ 4

Я пробовал это:

public:
   const std::vector<int const *> getResource()
   {
       return reinterpret_cast<std::vector<const int *> &>(resources);
   }

private:
   std::vector<unique_ptr<int>> resources;

Это сработало.