Witek
Witek

Reputation: 269

vector of unique_ptr in C++11

I recently switched to C++11 and I'm trying to get used to good practices there. What I end up dealing with very often is something like:

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

This requires me to do something like adding a _returnableVector and translating the source vectors to be able to return it later on:

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

Has anyone noticed similar problem? What are your thoughts and solutions? Am I getting the whole ownership idea right here?

UPDATE: Heres another thing: What if one class returns a result of some processing as vector<unique_ptr<HeavyResource>> (it passes the ownership of the results to the caller), and it is supposed to be used for some subsequent processing:

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

Upvotes: 16

Views: 22000

Answers (4)

MikeMB
MikeMB

Reputation: 21166

If you encounter this often, it might make sense to write a class, that behaves like a unique_ptr, but passes the constness of the pointer to the object it points to. That way, you can just return a const reference to your vector.

I ended up writing this once and be done with it:

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

Of course, you can extend it a bit, if you need custom deleters or want to add a specialization for managing arrays, but this is the base version. I also belive I've seen a more refined version of this somwhere here on SO, but I can't find it right now.

Here is an example, of how this can be used in a class:

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

EDIT

As far as your update is concerened, you have to live with the fact that vector<T> is unrelated to vector<B> even if it would be valid to cast T to B and vice versa.
You can write adaptors, that give you a different view to the elements (by casting each element when necessary) but - aside from creating a new vector of the proper type - there exists no general meachanism (that I am aware of) to do what you want.

Upvotes: 1

vincentB
vincentB

Reputation: 128

I tried this:

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

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

It worked.

Upvotes: -2

TartanLlama
TartanLlama

Reputation: 65770

I would suggest that instead of maintaining and returning an un-unique_ptred vector, you provide functions to access the elements directly. This encapsulates the storage of your resources; clients don't know that they are stored as unique_ptrs, nor that they are kept in a vector.

One possibility for this is to use boost::indirect_iterator to dereference your unique_ptr automatically:

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

Demo

Upvotes: 20

Tas
Tas

Reputation: 7111

You might want to consider shared_ptr instead, as it probably reflects what you are trying to achieve. You'll probably want to use unique_ptr in smaller scopes, or any time you wish to reflect only one thing using an object.

Upvotes: 0

Related Questions