The cppreference.com page states that a range-based for loop expression produces a code similar to the following (__range
, __begin
and __end
are for exposition only):
for ( range_declaration : range_expression ) loop_statement
{
auto && __range = range_expression ;
for (auto __begin = begin_expr, __end = end_expr; __begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
With that description, I deduce that the __begin
and __end
must be of the same type, in fact, this isn't your case. A pointer to Parent
isn't a pointer to Derived
, they must be casted explicitly and the range-based for loop does not perform any cast at all.
In order to achieve what you're looking for, I would create a helper class that performs the cast for me1, for example:
struct Helper
{
Helper(Parent *p) : obj(p) {}
Parent *const obj;
operator Derived *() { return static_cast<Derived *>(obj); }
};
This helper allows us to do the following:
std::vector<Parent*> objects { new Derived(), new Derived() };
for (Helper h : objects) {
Derived *d = h;
d->use();
}
We can templatize the helper class in order to use it on other Parent-Derived contexts:
template <typename T> struct THelper
{
THelper(Parent *p) : obj(p) {}
Parent *const obj;
operator T *() { return static_cast<T *>(obj); }
};
...
...
for (THelper<Derived> h : objects) {
Derived *d = h;
d->use();
}
for (THelper<Derived2> h : objects) {
Derived *d = h;
d->use();
}
But if your objects
vector contains mixed types, could be dangerous.
As a improvement, you can replace the static_cast
with a dynamic_cast
and check for nullptr
, but you must be aware of it's overhead.
This isn't a solution that I would use on a project of mine but I hope it looks clean and neat to you ;)
Edit
however, the conversion (whether explicit with static_cast
or through the Helper
class) is still required as an additional line inside the for loop.
As we talked before, the cast (static or dynamic) is mandatory, but if you're concerned about the neatness of the loop being truncated by the addition of an additional line, overloading the operator ->
should do the trick:
struct Helper
{
Helper(Parent *p) : obj(p) {}
Parent *const obj;
operator Derived *() { return static_cast<Derived *>(obj); }
Derived *operator ->() { return static_cast<Derived *>(obj); }
};
for (Helper h : objects) {
h->use();
}
Note that if you're using the operator ->
you cannot check for nullptr
, which would be necessary if your objects
vector have mixed derived types.
I've updated the live demoin order to include the new code.
1 Although, as we can read on the comments, there are better ways to achieve your goal.