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?

+3


source to share


3 answers


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 class Helper

) 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.

+2


source


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
        }
    }

      

+3


source


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?

+3


source







All Articles