Reputation: 41
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
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 class
es are struct
s at this point to avoid typing public
too often.
Upvotes: 4
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);
}
}
Upvotes: 1