C2280 or how do I make a list of Objects

Hi Guys,

I am a bit lost here and I believe my C++ skills need brushing up:

I have a component class:

class A : public juce::Component
{
public:
A()
{
}

~A()
{
}
}

I have a

std::vector myVector;

for (int i = 0;i++;i < 5)
        {
            myVector.clear();
            A  mNewInstance = A();
            mSteps.push_back (mNewInstance);
        }

which gets me C2280. I read a bit and I am wondering if this is the right way. I want to achieve the following:

I have an selectable number of components and I want to add them to a collection so I chose std:vector, as array is not dynamic. Or to make it more clear, all I want to do is dynamically create GUI Components depending on users choice.

Hi!
On quick glance, several issues here:

  • The vector has no datatype to hold
  • Components should not be copied
  • Avoid explicit memory management using new/delete

Ownership of the child components must be unique and well defined.
You could use a vector of std::unique_ptr, or juce::OwnedArray. If you later modify those, or if the parent component gets deleted, these data structures will take care of deleting the child component objects automatically.

Don’t forget addAndMakeVisible as well as setBounds on newly created components.

Do you want to change the number of child components during runtime, or is it just something you’re doing once when constructing the parent component?

Hi thanks for the answer,

I do not understand what you mean with the vector has no data type to hold?
And yes I want to create on runtime and change on runtime.

And I do not want to copy the component. I want to dynamically create instances and put them in a collection like and array. Maybe a short word on why:

I am trying to build a step sequencer. I am designing a hierarchical UI.

So there Is a sequencer window call it SqWindow
that has a number of bars SqBa
Each bar has a number of steps SqStep

The amount of bars and steps per bar can be changed at runtime so it needs te be dynamic.

TYVM,

Jens

C++ is a strong typed language, therefore you have to specify the type of elements that a std::vector should hold as template argument. So in your case, this should be

std::vector<A> myVector;

If your vector is specified like that, you cannot e.g. add an integer like myVector.push_back (42); because you created a vector explicitly intended to hold objects of type A.

That being said, since C++ 17 you can under some circumstances drop the explicit template type, as class template argument deduction allows to automatically figure out the type when you assign values to it right away. Like

std::vector myVector { A(), A(), A() }; // will be deduced to myVector<A>
std::vector numbers { 42, 43, 44 }; // will be deduced to myVector<int>

But that usually only works when creating temporary vectors by assigning values directly to them, not when they are declared as empty vectors and elements are added later on.

Then you have chosen the wrong approach with your code above. If you do

A  mNewInstance = A();
mSteps.push_back (mNewInstance);

Then mNewInstance is created and in the call to push_back a copy of mNewInstance is made and added to the vector. Now if you look at the juce_Component.h source code you’ll find a JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Component) which is a strong hint that copying objects of type juce::Component is not possible, which then also applies to types derived from juce::Component.

So you strategy should be creating an instance once on the heap, wrap it in a smart pointer for lifetime management and then just move around that smart pointer.

Besides all this, your code has some further errors. First of all your for loop declaration is wrong. The condition for the loop end has to be before the increment. So right is

for (auto i = 0; i < 5; ++i)

Then you call myVector.clear(); in each loop iteration, which will basically remove everything you put in the vector before. Most likely not what you want :wink:

TLDR, this could be your code:

// Declared as a member variable
std::vector<std::unique_ptr<A>> myVector;

// In your initialisation function
myVector.clear();
for (auto i = 0; i < 5; ++i)
{
    auto myNewInstance = std::make_unique<A>();
    mSteps.push_back (std::move (myNewInstance));
}

Chances are, the forum choked your <A>, since that text was not formatted to allow < because it can be confused with html.

It doesn’t matter if you want to copy the component or not, the vector will try to do this and fail, when you call push_back().
The common workaround is to wrap the component in std::unique_ptr, which looks like:

std::vector<std::unique_ptr<A>> myVector;

auto mNewInstance = std::make_unique<A>();
mSteps.push_back (std::move (mNewInstance));

You can use -> and * on the unique_ptr just like any normal pointer.

EDIT: either @PluginPenguin ninja edited, or I cannot read, sorry for double post… :wink:

Thank you! This makes it clear!