C ++ 11 for looping on derived objects
If I have a vector of pointers to the parent class, and the vector is initialized by instantiating objects derived from the parent class, then it seems that I cannot use a range based loop to get the elements as derived objects. Here's a simple example:
#include <vector>
class Parent {};
class Derived : public Parent {};
int main()
{
std::vector<Parent*> objects { new Derived(), new Derived() };
for (Derived* d : objects) { // ERROR
// Use d
}
return 0;
}
Is there a clean way to do what I want (i.e. iterate over derived objects)? I know can do something like this:
for (Parent* p : objects) {
Derived* d = static_cast<Derived*>(p);
// Use d
}
But is this the cleanest way since C ++ 11? What other alternatives?
source to share
On page cppreference.com stated that the expression based on the range for the cycle , generates code similar to the following ( __range
, __begin
and __end
are only intended to present):
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
}
}
In this description, I go out that __begin
they __end
should be of the same type, in fact, this is not your case. A pointer to is Parent
not a pointer to Derived
, they must be explicitly set, and a range based loop does not perform any casts.
To achieve what you are looking for, I would create a helper class that does the casting for me 1 like:
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 to use it in other contexts obtained by the parent:
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 vector objects
contains mixed types, it can be dangerous.
As an improvement, you can replace static_cast
with dynamic_cast
and check nullptr
, but you should know its service data .
This is not a solution I would use on my project, but I hope it looks clean and tidy to you;)
Edit
however, the conversion (whether explicitly from
static_cast
or via a classHelper
) is still required as an extra line inside the for loop.
As we said earlier, cast (static or dynamic) is a must, but if you're worried about the loop being truncated by adding an extra line, the overload 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, if you are using operator ->
, you cannot check nullptr
what would be necessary if your vector objects
had mixed derived types.
I've updated the live demo to include the new code.
1 Although, as we can read the comments, there are better ways to achieve your goal.
source to share
You can use dynamic_cast
to achieve this goal:
class Parent {
public:
virtual ~Parent() { } // make class polymorphic
};
class Derived : public Parent {};
int main()
{
std::vector<Parent*> objects { new Derived(), new Derived() };
for (Parent* d : objects) { // ERROR
auto temp = dynamic_cast<Derived*>(d);
if (d) // cast succeeded
{
// we found the right class
}
}
source to share
Range iterators are syntactically cleaner and you cannot use in a loop for
.
for (Parent* p : objects) {
Derived* d = static_cast<Derived*>(p);
// Use d
}
If you subvert at all, it must be done within the body. A well-designed program doesn't really require step-down casting. Be careful!
PS: Prefers dynamic_cast
more static_cast
when dropped down. But the low cost of RTTI when used dynamic_cast
. Checkout - Performance of dynamic_cast?
source to share