Phil
Phil

Reputation: 627

How to retrieve multiple inherited types from a collection in C++?

Say you have a std::vector class that holds type Item, and you store inherited types of items: Weapon, Potion, Armor, etc. How do you retrieve the item as the inherited class and not the base?

Upvotes: 2

Views: 100

Answers (2)

Jonathan Mee
Jonathan Mee

Reputation: 38939

Your design has a bad smell in it. Storing item* has the unfortunate cost of having to manage them outside the vector. The only good motivation for a vector<item*> would be to use the items polymorphically. By trying to detect the type you're demonstrating that you are not using them polymorphically.

A better solution would be to store these items in separate vectors, rather than a communal vector<item*> items, so vector<weapon> weapons, vector<potion> potions, vector<armor> armors.

So please don't use this code, but given vector<item*> items you can do this:

for(auto i : items) {
    if(dynamic_cast<weapon*>(i) != nullptr) {
        auto iWeapon = static_cast<weapon*>(i);
        // work with weapon here
    } else if(dynamic_cast<potion*>(i) != nullptr) {
        auto iPotion = static_cast<potion*>(i);
        // work with potion here
    } else {
        assert(dynamic_cast<armor*>(i) != nullptr);
        auto iArmor = static_cast<armor*>(i);
        // work with armor here
    }
}

Upvotes: 2

Vittorio Romeo
Vittorio Romeo

Reputation: 93334

How do you retrieve the item as the inherited class and not the base?

This suggests that you need closed-set polymorphism, which is provided by std::variant or boost::variant.

using Entity = std::variant<Weapon, Potion, Armor>;
// An `Entity` is either a `Weapon`, a `Potion`, or an `Armor`.

std::vector<Entity> entities;

struct EntityVisitor
{
    void operator()(Weapon&);
    void operator()(Potion&);
    void operator()(Armor&);
};

for(auto& e : entities)
{
    std::visit(EntityVisitor{}, e);
    // Call the correct overload depending on what `e` is.
}

Upvotes: 6

Related Questions