How to resize a vector of vector of unique_ptr?
Asked Answered
M

3

6

How to properly resize a vector of unique_ptr vectors in one line without gcc giving a compilation error regarding a deleted function?

vector<vector<unique_ptr<A> >> a;
a.resize(..... )

UPDATE: This is the code that I used that works OK.

  int width, height;
  vector<vector<unique_ptr<A> >> a;
  a.resize(width);
  for (int i = 0; i < width; i++) {
      for (int j = 0; j < height; j++) {
          a.at(i).emplace_back(new A());

If possible, I would like to resize the sizes in one go just like resizing of vector of vectors;

vector<vector<int>> intObj;
intObj.resize(width, vector<int>(height, int()));

but I got this error whenever I tried to resize the above vectors using the following method;

a.resize(x, vector<unique_ptr<A>>(y, unique_ptr<A>()));

error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = A; _Dp = std::default_delete<A>]’

Thanks.

Manipular answered 27/11, 2015 at 8:27 Comment(8)
vector<vector<...>> is a code smell to me in this scenario. You should use a single vector that is width*height in size, not a vector of vectors. NOTE: If you make this change, a call to a.resize(width*height) will work.Mauretania
Exactly what code did you try, and what error did you get?Fit
@Mauretania I tend to disagree. vector<vector<>> is more scalable as the memory used to held data doesn't need to be continuous which might be necessary for a large amount of data. Also I have seen way to many wrong calculated offsets...Inexplicit
@Mauretania The one-argument version may well work.Fit
BTW, emplace_back(new A()) = leak. Don't do it; use push_back(make_unique<A>())Fit
@Fit right, I was just considering the overloads that took a count and a value. Realise I didn't word it very well.Mauretania
@TC Thanks, I didn't know that. Also I updated the question with the gcc error output.Manipular
@Manipular Perhaps you could just define a new overload that makes use of move?Balzac
M
5

The problem is that vector<unique_ptr<A>> is a non-copyable type, because unique_ptr<A> cannot be copied. a.resize(x, vector<unique_ptr<A>>(y)) uses the copy constructor of the vector<unique_ptr<A>> and a.resize(x, vector<unique_ptr<A>>(y, unique_ptr<A>())) is even worse as it uses the copy constructor of both unique_ptr<A> and vector<unique_ptr<A>>.

Two solutions:

  1. Use a single vector<unique_ptr<A>> that has size x*y. This means a.resize(x*y) will work as intended.
  2. Or, you will need to use a loop over all of the sub vectors.

Example of loop:

a.resize(x);
for (auto& i : a) {
    i.resize(y);
}

EDIT: If you wanted the unique_ptr<A>s to point at default constructed As and not nullptr, then you can use this code:

a.resize(x);
for (auto& i : a) {
    i.reserve(y);
    for (int j = 0; y != j; ++j) {
        i.push_back(std::make_unique<A>());
    }
}

You can't use a single resize call to do what you want if you maintain your current design.

EDIT 2: Here's a one liner for default constructing As if you go with solution 1:

std::generate_n(std::back_inserter(a), x*y, []{ return std::make_unique<A>(); });
Mauretania answered 27/11, 2015 at 8:50 Comment(5)
Wouldn't this still result in all unique_ptrs being initiliazed with nullptr?Inexplicit
@SimonKraemer that's what his original code for a.resize(x, vector<unique_ptr<A>>(y, unique_ptr<A>())) was trying to do. I see what he really wants is default constructed As, so I'll edit in a bit.Mauretania
@Mauretania Thanks. I guess I have to go with the second solution, otherwise other parts of the code will have to be changed as well. So I guess there's no way to resize a vector of vectors of non-copyable type since the resizing invokes copy constructor, unless I define a new overload that makes use of move?Manipular
@Manipular I don't know what your overload that uses move would do, but note that my solution creates unique_ptrs and then moves them in to the vector. An alternative is to use some kind of value_ptr instead of unique_ptr (read up on it) that performs a deep copy when copied.Mauretania
A thousand thanks for the information, that the operation would (in addition to the classes move constructor/ move =oprtator) require a vector<unique_ptr<A>> (move) constructor. That clarifies things a lot imho. Upvote also for the one-line solution (I forgot about the back_inserter...)Sputnik
M
0

I understand the question is asking about something more involved (vector of vectors of unique_ptrs), but although my case was simpler, the search engine led me here:

I wrote:

std::vector<std::unique_ptr<MyData>> myVector;
myVector.resize(myNumber, nullptr);

And it didn't compile.

So for all the folks led here the same way, the solution to this much simpler case is:

myVector.resize(myNumber);

I.e. as soon as you add the default value, even if it's std::nullptr_t, the code doesn't compile.

Martinsen answered 10/9 at 7:23 Comment(0)
M
-4

std::unique_ptr is not copyable use std::shared_ptr for full container support.

Mestizo answered 27/11, 2015 at 8:44 Comment(1)
...and create a container of w*h pointers all to the same one object?Fit

© 2022 - 2024 — McMap. All rights reserved.