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.
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.
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.
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.
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.
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…
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.