I want to store objects relative to their type.
I have a Status
class which is inherited to create different status such as Burn
, Stun
, etc...
I would like to store statuses in sets with a set for each type (a character can a multiple burn status at once, so I want to get the set storing all the burn statuses but not other statuses).
my solutions so far looks like this
std::map<std::type_index, std::set<Status*>> statuses;
// access all Burn statuses
for (const Burn* b : statuses.find(typeid(Burn))->second) {} // error : E0144 a value of type "Status *" cannot be used to initialize an entity of type "const DamageModifier *"
**
However this is downcasting and the compiler doesn't want it to work.
My questions are as follow:
- How could I access a set and downcast it to the right type without copying
- Do you think of any other way to store all statuses of the same subtype together with easy access ?
- I dont want to store it all in a set and dynamic cast because it starts getting expensive
- I dont want to declare one set by hand for each new
Status
subclass.
Edit :
The problem was that I tried to do two things at once from my last code version
- Switching from
std::set<Status*>
tostd::map<std::typeid, std::set<Status*>>
which is OK as long as you cast the result the same as before - Trying to implicitly cast a whole set of pointer which is useless and naive, at least in this context
Both answer helped me realise the problem was that I tried to do both at once.
CodePudding user response:
How could I access a set and downcast it to the right type without copying
You can use static_cast
to down cast:
for (const Status* s : statuses.find(typeid(Burn))->second) {
auto b = static_cast<const Burn*>(s);
}
You must be very careful though to not insert pointers to wrong derived classes into wrong set. That will silently pass compilation and break at runtime (if you're lucky).
CodePudding user response:
You need to have a cast somewhere. If you've gated insertion into statuses
by e.g. void add_status(Status * status) { statuses[typeof(status)].insert(status); }
, then you can safely static_cast
, otherwise you have to be wary, as incorrectly static_cast
ing has undefined behaviour.
If you have a lot of places where you cast collections, I'd be tempted to write some _view_cast
templates.
template <typename To>
struct static_view_cast_t
{
template <std::ranges::view View>
auto operator()(View view) {
return view | std::ranges::views::transform([](auto & from) -> To { static_cast<To>(from); });
}
template <std::ranges::view View>
friend auto operator|(View view, static_view_cast_t c) {
return c(view);
}
};
template <typename To>
constexpr static_view_cast_t static_view_cast;
And so on for each cast you need.