Agnelo
Agnelo

Reputation: 41

How to write overloaded functions for derived classes that are stored in a std::vector as the base class

Been experimenting and searching for how to do this and would like some help....

Goal : Have a std::vector store either Cat and Bear objects (like a heterogenous container). Then iterate the vector and perform an overloaded function A or function B depending on type (Cat/Bear)

Solution : I created a base class (Animal), the std::vector stores shared_ptrs of this type. I create Animal derived Cat or Bear objects (new), cast their pointers to shared_ptr<Animal> and push to vector.

Problem : I have two processing functions that take Cat or Bear parameters respectively. But I need to std::static_pointer_cast<Bear> or std::static_pointer_cast<Cat> the Animal pointers from the vector before passing to the function. I want the correct function to be called automatically (due to overloading).

thanks

class AnimalClass
{
   AnimalType type;
};

class Bear : public AnimalClass
{

}

class Cat : public AnimalClass
{
}

void action(Bear* bear)
{
  run();
}

void action(Cat* cat)
{
  stroke();
}


int main()
{
std::vector<shared_ptr<AnimalClass>> store;

// Populate store

Cat* cat= new Cat();
store.push_back(shared_ptr<AnimalClass>)cat );

Bear* bear= new Bear();
store.push_back(shared_ptr<AnimalClass>)bear );

// Process store

for (auto item : store)
{
   action(item);   // Does not compile unless item is static_pointer_cast to Bear or Cat, don't want to do that.
}

}

Upvotes: 2

Views: 224

Answers (2)

lubgr
lubgr

Reputation: 38305

The idiomatic solution ist to use the virtual dispatch that C++ already offers out of the box. You need to turn the so far free functions into virtual member functions and override them in derived classes. Example:

#include <cstdio>

struct Animal
{
    virtual void action() = 0;
};

struct Cat : Animal {
    void action() override
    {
        std::puts("Cat::action");
    }
};

struct Bear : Animal {
    void action() override
    {
        std::puts("Bear::action");
    }
};

You can use these as depicted in the following.

std::vector<std::shared_ptr<Animal>> store;

store.push_back(std::make_shared<Cat>());
store.push_back(std::make_shared<Bear>());

for (auto& item : store)
    item->action();

Note that I have used std::make_shared in favor of a raw new. And note that all classes are structs at this point to avoid typing public too often.

Upvotes: 4

Jarod42
Jarod42

Reputation: 217930

As alternative to traditional virtual method way, you might use std::variant:

struct Bear
{
    void run() { std::cout << "run\n"; }
};

struct Cat
{
    void stroke() { std::cout << "stroke\n"; }
};

void action(Bear& bear)
{
    bear.run();
}

void action(Cat& cat)
{
    cat.stroke();
}

using Animal = std::variant<Cat, Bear>;

int main()
{
    std::vector<Animal> store{Cat{}, Bear{}};

    // Process store
    for (auto& animal : store)
    {
        std::visit([](auto& animal){ action(animal); }, animal);
    }
}

Demo

Upvotes: 1

Related Questions