tags:

views:

96

answers:

7

Basically I want to reverse iterate through 2 std::vectors. a layer has a vector of shapes.

usually I could do something like this:

for(int i = 0; i < layers.size(); ++i)
{
   for(int j = 0; j < layers[i].shapes.size(); ++j)
   {
      layers[i].shapes[j].dosomething();
   }
}

However right now I need to reverse iterate through the vectors and therefore need to use a reverse iterator but how would this work? Since iterators kind of make it like a for each, how can I acces the current shape of the current layer being iterated through? Thanks

+5  A: 

The easy way:

for(int i = layers.size() - 1; i >= 0; --i)
{
   for(int j = layers[i].shapes.size() - 1; j >= 0; --j)
   {
      layers[i].shapes[j].dosomething();
   }
}

The "proper" way:

for(vector<Layer>::reverse_iterator i = layers.rbegin(); i != layers.rend(); ++i)
{
   for(vector<Shape>::reverse_iterator j = i->shapes.rbegin(); j != i->shapes.rend(); ++j)
   {
      j->dosomething();
   }
}
sje397
No, with std::vectors, this will skip an element
Milo
@Jex: no, it won't
sje397
`rbegin` returns a `reverse_iterator`
Naveen
@Naveen - fixed 3 mins before your comment :)
sje397
The easy way is wrong. If you ever have a buffer of size greater than 2GB (for most compilers on most platforms), this will either corrupt memory or assert() you to hell. int != size_t
Clark Gaebel
@Clark Gaebel: It's not wrong at all. Most of the time you can guarantee elsewhere in the program that MAX_INT is never reached. I don't think the usual program dealing with layers and shapes, especially if you treat *every one* as done here (rather than e.g. selecting relevant ones with something like an octree), is going to be much use if it has that many entities.
sje397
Vector has a guarantee that it holds less than or equal to the maximum value of size_t (look at the definition of the "size" function). Why would you cut its effective range in half? That first example should NEVER be used. And honestly, no one is going to check that the size is less than or equal to MAX_INT. As soon as the vector has 2^32 elements, you will have an infinite loop. Good luck tracking THAT bug down.
Clark Gaebel
@Clark Gaebel: My first point is, with that many entities, good luck getting the program to *run* let alone spin lock. Second is, I wouldn't presume to know about the OPs program - he used 'int' for the forward iteration counter, and the question is not about that. For all we know, his 'layer/shape loader' can only load MAX_INT entities. It's worth the warning, sure, but it's not necessarily wrong and it's always bad to get too dogmatic.
sje397
@Clak Gaebel: and btw, it won't go into an infinite loop - it'll stop as soon as the counter wraps to negative...which is probably harder to trace :)
sje397
Oops, that's right.
Clark Gaebel
A: 

If you're using indices for forward iteration why not for reverse? ie

for( int i = layers.size()-1; i>=0; --i )
c-urchin
A: 

You don't really need iterators (reverse or otherwise) for this. If you like the code you have right now, and just want it to run in reverse, you can do that fairly directly:

for (int i=layers.size()-1; i>-1; --i)
    for (int j=layers[i].shapes.size()-1; j>-1; --j)
        layers[i].shapes[j].dosomething();

Of course, you can use iterators if you prefer, but in this case I think it'll make the code longer and more complex overall, without adding much:

std::vector<shape>::reverse_iterator this_shape;
std::vector<layer>::reverse_iterator this_layer;

for (this_layer = layers.rbegin(); this_layer != layers.rend(); ++this_layer)
   for (this_shape = this_layer->shapes.rbegin(); this_shape != this_layer->shapes.rend(); ++this_shape)
       this_shape->dosomething();
Jerry Coffin
A: 
for(std::vector<mytype>::reverse_iterator i = layers.rbegin(); i != layers.rend(); ++i)
{
   for(std::vector<myothertype>::reverse_iterator j = i->shapes.rbegin(); j != i->shapes.rend(); ++j)
   {
      j->dosomething();
   }
}

Iterators are more flexible this way than index-based - it's far simpler to change begin/rbegin, end/rend, and reverse_iterator/iterator,to change the iteration direction. This becomes even easier if you use this code often and can just stick a typedef somewhere for it and definitely best if you've got C++0x or can use an inline function for the automatic type deduction.

DeadMG
A: 
Phil Nash
A: 

Iterators to containers allow you to access the "current" object using the the * or -> operators - in that sense iterators are like pointers to the vector's elements.

For instance, if iter is an iterator on a vector of layers then you can access the layer currently pointed by the iterator using:

layer = *(iter);

or

nshapes = iter->shapes.size();
smichak
+1  A: 

Use reverse iterators.

typedef std::vector::<Layer>::reverse_iterator LayerIt;
for(LayerIt layerIt = layers.rbegin(); layerIt != layers.rend(); ++layerIt) // reverse-iterator
{
   Layer& layer = *layerIt;

   typedef std::vector<Shape>::reverse_iterator ShapeIt;
   std::vector<Shape>& shapes = layer.shapes;

   for(ShapeIt shapeIt = shapes.rbegin(); shapeIt != shapes.rend(); ++shapeIt) // reverse-iterator
   {
     Shape& shape = *shapeIt;
     shape.dosomething();
    }
}

See std::vector's rbegin() and rend() functions : http://www.cplusplus.com/reference/stl/vector/ I decomposed the code to make it more obvious, tell me if it's not. If you don't know about iterators, you'll have to search :)

Notice that if you use a recent compiler with the new auto features, it's simpler to write :

for(auto layerIt = layers.rbegin(); layerIt != layers.rend(); ++layerIt) // reverse-iterator
{
   Layer& layer = *layerIt;
   std::vector<Shape>& shapes = layer.shapes;

   for(auto shapeIt = shapes.rbegin(); shapeIt != shapes.rend(); ++shapeIt) // reverse-iterator
   {
     Shape& shape = *shapeIt;
     shape.dosomething();
    }
}
Klaim
That should be 'reverse_iterator' shouldn't it?
sje397
Yes, fixed now.
Klaim