web-dev-qa-db-fra.com

vecteur de unique_ptr en C++ 11

Je suis récemment passé à C++ 11 et j'essaie de m'habituer aux bonnes pratiques dans ce domaine. Je finis souvent par avoir à faire face aux problèmes suivants:

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

Cela nécessite que je fasse quelque chose comme ajouter un _returnableVector et traduire les vecteurs source pour pouvoir le renvoyer ultérieurement:

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

Quelqu'un at-il remarqué un problème similaire? Quelles sont vos pensées et solutions? Est-ce que je reçois l'idée de la propriété entière ici?

UPDATE: Voici une autre chose: Et si une classe renvoie le résultat d’un traitement sous la forme vector<unique_ptr<HeavyResource>> (elle transmet la propriété des résultats à l’appelant), et est supposée être utilisée pour certains traitement ultérieur:

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

Je suggérerais qu'au lieu de conserver et de renvoyer un vecteur non -unique_ptred, vous fournissiez des fonctions pour accéder directement aux éléments. Cela encapsule le stockage de vos ressources; les clients ne savent pas qu'ils sont stockés sous la forme unique_ptrs, ni qu'ils sont conservés dans une vector.

Une possibilité consiste à utiliser boost::indirect_iterator pour déréférencer automatiquement votre 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); }

Démo

17
TartanLlama

Si vous rencontrez cela souvent, il peut être judicieux d'écrire une classe, qui se comporte comme un unique_ptr, mais passe la constness du pointeur à l'objet vers lequel elle pointe. De cette façon, vous pouvez simplement renvoyer une référence const à votre vecteur. 

J'ai fini par écrire ceci une fois et en finir avec ça:

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

Bien sûr, vous pouvez l’étendre un peu, si vous avez besoin de programmes spéciaux de suppression ou si vous souhaitez ajouter une spécialisation pour la gestion des tableaux, mais il s’agit de la version de base. Je pense aussi avoir vu une version plus raffinée de ceci quelque part ici sur SO, mais je ne la trouve pas pour le moment.

Voici un exemple de la façon dont ceci peut être utilisé dans une classe:

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

MODIFIER

En ce qui concerne votre mise à jour, vous devez accepter le fait que vector<T> n’est pas lié à vector<B> même s’il serait valide de convertir T en B et inversement.
Vous pouvez écrire des adaptateurs qui vous donneront une vue différente des éléments (en lançant chaque élément si nécessaire), mais - mis à part la création d’un nouveau vecteur du type approprié - il n’existe aucun mécanisme général (à ma connaissance de) faire ce que vous voulez. 

1
MikeMB

Vous voudrez peut-être envisager plutôt shared_ptr, car il reflète probablement ce que vous essayez d'atteindre. Vous voudrez probablement utiliser unique_ptr dans des portées plus petites, ou chaque fois que vous souhaitez refléter une seule chose à l'aide d'un objet.

0
Tas