If there is a method, which aims to put objects with specific type into another list, how can I use a user-defined parameter in the dynamic_cast
? I know, that I can not use the std::string parameter directly in dynamic_cast
, but is there a better solution?
std::list<Car*> GetCarsOfType(std::string type)
{
std::list<Car*> carsOfType;
for (int i = 0; i < cars.size(); i)
{
if (dynamic_cast<type> cars[i]) // This is not possible, but how can I solve this in a better way?
{
carsOfType.push_back(cars[i]);
}
}
return carsOfType;
}
CodePudding user response:
You can create a pure virtual
function in the class Car
that every class that inherits must implement:
#include <list>
#include <string>
#include <iostream>
class Car
{
public:
virtual std::string gettype() const = 0;
virtual ~Car() = default;
};
class Ford: public Car
{
std::string gettype() const { return "Ford";}
};
class VW: public Car
{
std::string gettype() const { return "VW";}
};
std::list<Car*> cars {new Ford{}, new VW{}, new Ford{}};
std::list<Car*> GetCarsOfType(std::string type)
{
std::list<Car*> carsOfType;
for (const auto &car: cars)
{
if (car->gettype() == type)
{
carsOfType.push_back(car);
}
}
return carsOfType;
}
int main()
{
std::cout << GetCarsOfType("Ford").size() << std::endl;
std::cout << GetCarsOfType("VW").size() << std::endl;
std::cout << GetCarsOfType("BMW").size() << std::endl;
}
As a bonus here the compile time version, create a template function that filters for the template type:
#include <list>
#include <iostream>
class Car
{
public:
virtual ~Car(){}
};
class Ford: public Car {};
class VW: public Car {};
class BMW {};
std::list<Car*> cars {new Ford{}, new VW{}, new Ford{}};
template <class T>
std::list<Car*> GetCarsOfType()
{
std::list<Car*> carsOfType;
for (const auto &car: cars)
if (dynamic_cast<T*>(car))
carsOfType.push_back(car);
return carsOfType;
}
int main()
{
std::cout << GetCarsOfType<Ford>().size() << std::endl;
std::cout << GetCarsOfType<VW>().size() << std::endl;
std::cout << GetCarsOfType<BMW>().size() << std::endl;
}
CodePudding user response:
You could keep a central repository of the mapping between the named car type and the actual type if you wish. It could then be used to do the dynamic_cast
test for you.
Example:
#include <algorithm>
#include <unordered_map>
std::list<Car*> GetCarsOfType(std::string type) {
// map between the named type and the dynamic_cast test:
static const std::unordered_map<std::string, Car*(*)(Car*)> cast_checkers{
{"SUV", [](Car* c) -> Car* { return dynamic_cast<SUV*>(c); }},
{"Limo", [](Car* c) -> Car* { return dynamic_cast<Limo*>(c); }}
};
std::list<Car*> carsOfType;
// a filter that only returns true for those you want
auto flt = [&type](Car* c) { return cast_checkers.at(type)(c) != nullptr; };
std::ranges::copy_if(cars, std::back_inserter(carsOfType), flt);
return carsOfType;
}
Or using a view
:
#include <ranges>
#include <unordered_map>
std::list<Car*> GetCarsOfType(std::string type) {
static std::unordered_map<std::string, Car* (*)(Car*)> cast_checkers{
{"SUV", [](Car* c) -> Car* { return dynamic_cast<SUV*>(c); }},
{"Limo", [](Car* c) -> Car* { return dynamic_cast<Limo*>(c); }}};
// same filter as above:
auto flt = [&type](Car* c) { return cast_checkers.at(type)(c) != nullptr; };
// a view over those you want
auto matchview = cars | std::views::filter(flt);
// populate the container using the view:
return {matchview.begin(), matchview.end()};
}