Question

I'm relatively new to C++, and coming from a C# background I'm having trouble with this list iteration:

I have one method which loops through a list of objects and calls an update method for each one, which works great. The list is of type std::list<EngineComponent> and is called engineComponents.

void Game::Update()
{
    for (EngineComponent component: this->engineComponents)
    {
        component.Update();
    }
}

I also have a subclass of EngineComponent called DrawableEngineComponent.

The issue appears when I try to do a similar iteration:

void Game::Draw()
{
    for (DrawableEngineComponent component: this->engineComponents)
    {
        component.Draw();
    }
}

This produces the error "No suitable user-defined conversion from 'EngineComponent' to 'DrawableEngineComponent' exists". Given that this implementation was all fine and dandy in C#, I'm not really sure how best to address this issue in C++.

I can think of a few alternative ways that would/should work, but I'm wondering if there's functionality in C++ to do this in a way similar to C# without having to manually define a conversion.

The definitions for the two classes concerned are as follows:

class EngineComponent
{
public:
    EngineComponent(void);
    ~EngineComponent(void);

    virtual void Update(void);
};


class DrawableEngineComponent : public EngineComponent
{
public:
    DrawableEngineComponent(void);
    ~DrawableEngineComponent(void);

    virtual void Draw(void);
};

And yes, I am copying the XNA framework a bit ;)

Was it helpful?

Solution

The actual reason you get that error is that the way you have defined the range-based for, you are retrieving objects by copy rather than reference:

for (EngineComponent component: this->engineComponents)
{
     // component is a copy of the object in the list
}

EngineComponent is a super-class and so there's no implicit cast to a derived class. If you try to copy a DrawableEngineComponent out of a list of EngineComponent the compiler has no way of knowing if the source object is really a derived class.

Standard containers don't really handle polymorphic objects very well. A much better solution is to use std::shared_ptr to store pointers to the objects.

std::list<std::shared_ptr<EngineComponent>> myList;
myList.push_back(std::make_shared<DrawableEngineComponent>());

This will wrap a DrawableEngineComponent in a shared pointer and store it in the list. It can be accessed in a similar way to your original method:

for (auto& component: engineComponents)
{
    component->Update();
}

But this time you have a fully polymorphic object you can call. If the object overloads the Update() method in a sub-class then it's this that will be called. You can also use casting to get a pointer to the sub-class if that's what you need:

for (auto& component: engineComponents)
{
    auto pDrawComponent = dynamic_cast<DrawableEngineComponent*>(component.get());
    if (pDrawComponent)
    {
        // it's drawable
    }
}

OTHER TIPS

The std::list<EngineComponent> is just that; a list of engine component objects. When you push onto the list, you are making a copy of the object you are pushing. Unless you have defined a conversion between the sub-class and its base it will fail. Likewise trying to convert from the base to the sub-class as you are doing will also fail.

What you probably want is a list of pointers to base class objects, i.e.: A std::list<unique_ptr<EngineComponent>> would probably do the trick. Whatever type of pointer you use, you will need to downcast it to DrawableEngineComponent before you can call the Draw method:

for (unique_ptr<EngineComponent> & engCompPtr: engineComponents)
{
   DrawableEngineComponent & drawableEngComp = dynamic_cast<DrawableEngineComponent &>(*engCompPtr);
   drawableEngComp.Draw();
}

I don't know a great deal about C#, but I assume when you are working with objects, it is actually implemented by smart pointers of some description.

std::list<EngineComponent> stores a bunch of EngineComponents. If you add a DrawableEngineComponent to the list, you are slicing the "drawable" part off of it:

std::list<EngineComponent> engineComponents;
EngineComponent comp1;
engineComponents.push_back(comp1); // no problem, just copies it into the list
DrawableEngineComponent comp2;
EngineComponent newComp2 = *(EngineComponent*)(&comp2); // the drawable part is now sliced out!
engineComponents.push_back(newComp2); // this adds the sliced version to the list

More than likely, you are wanting to store a list of pointers to EngineComponents; but even with that, it would be advisable to store the drawable ones separately (otherwise you will have to do some casting and checking to make sure it can be casted to that type.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top