2

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?

Phil
  • 607
  • 1
  • 8
  • 22
  • 9
    You need a vector of pointers/references to avoid slicing. Then you need some way to detect the type of the object, either RTTI or a type flag. But the whole point of polymorphism is that you shouldn't need to know exact types. – cdhowie Jun 06 '17 at 14:36
  • 1
    [Try visiting the items instead](https://stackoverflow.com/questions/10116057/visitor-pattern-explanation) – StoryTeller - Unslander Monica Jun 06 '17 at 14:40

2 Answers2

6

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.
}
Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
2

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
    }
}
Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288