Adding components to std::vector with emplace_back

I’m trying to add Components to a std::vector with emplace_back, to avoid copying. However, it requires a move or copy constructor. Without a move constructor, I get:

copy constructor of 'PanelWindow' is implicitly deleted because base class 'juce::Component' has a deleted copy constructor

With the move constructors declared as default I get:

copy constructor is implicitly deleted because 'PanelWindow' has a user-declared move constructor

Is there a way I can add Components to a std::vector like this, without resorting to pointers?

The problem here is not the emplacement, but the fact that vector can only store elements that are movable (or copyable). vector wouldn’t really work with non-copyable elements, because the elements have to be moved to a new storage area after expanding the capacity of the vector.

Component is non-copyable and non-moveable, so vector<unique_ptr<Component>> is probably going to be the simplest working alternative to a plain vector<Component>.

Another alternative would be to use a collection type which doesn’t require its elements to be moveable, like list. You’d have to benchmark to find out whether the extra indirections when traversing the list are cheaper than the indirections from accessing elements in a vector<unique_ptr<>> though.

2 Likes

Thanks for the explanation. Converting to list was the quick solution, but vector looks like a better option when you need to access different elements.

You might want to look at https://docs.juce.com/master/classOwnedArray.html. It’s a vector like container that manages the deletion of the pointers for you. So no unique pointer required.

Example:

OwnedArray<PanelWindow> panels;
panels.add(new PanelWindow());

Thanks, I’m aware of OwnedArray, trying to use STL containers where possible (partly to understand them better)

Also I’m trying to avoid using new !

Looks like its now also possible to do e.g.

OwnedArray<PanelWindow> panels;
panels.add (std::make_unique<PanelWindow>());

which is much nicer :slight_smile:

Perfect, I didn’t know about that overload. It’s definitely much better, also exception save.

The advantage of OwnedArray<T> over vector<unique_ptr<T>> is that the memory is contiguous in the OwnedArray and not in the other.

This probably doesn’t have a big performance impact but depends on the number of stored elements and how often they need to be accessed.

1 Like

What do you mean the memory is contiguous? In both scenarios, you’ll have a contiguous region containing pointers, but the pointed-to objects could live anywhere on the heap. I believe OwnedArray and the vector approach are practically identical in this respect.

2 Likes

Also worth pointing out that the unique ptr version of add is not exception safe. It releases the pointer and then calls the raw-pointer overload of add. If adding the pointer at this point were to cause the underlying storage to resize, but that resize were to fail, then bad_alloc would be thrown and that object would be leaked. The vector solution does not suffer from this defect.

2 Likes
  1. I think you are correct, I thought that I read somewhere that the OwnedArray manages the allocations so that they are contiguous objects. But after the reading the source this seems not to be the case.

  2. Also, after reading the code: Yes you are correct.

There is a difference, but I cannot see, how that would play a role in practice:

In OwnedArray it is the container, that takes care of deleting the objects, vs in std::vector<std::unique_ptr<T>> each element in the vector is managing the lifetime of the pointee. Btw. the deletion is a bit weird, but sure it works…

…now we are finally OT :wink:

2 Likes

I didn’t realize this. Is there any way around it, or should we avoid using this function?

Recovering from these kinds of low-memory exceptions is unlikely to be necessary in the majority of apps, so this is probably not a big issue in practice: If your program isn’t able to allocate memory, then it likely won’t have any option but to quit anyway, at which point all the memory it was using will be returned to the system. If you’re working in a very constrained environment where low memory is a real possibility and you need to guarantee high fault tolerance, then maybe you should avoid OwnedArray, but otherwise I wouldn’t worry about it.

1 Like