Angus
Angus

Reputation: 1350

C++ STL - iterate through everything in a sequence

I have a sequence, e.g

std::vector< Foo > someVariable;

and I want a loop which iterates through everything in it.

I could do this:

for (int i=0;i<someVariable.size();i++) {
    blah(someVariable[i].x,someVariable[i].y);
    woop(someVariable[i].z);
}

or I could do this:

for (std::vector< Foo >::iterator i=someVariable.begin(); i!=someVariable.end(); i++) {
    blah(i->x,i->y);
    woop(i->z);
}

Both these seem to involve quite a bit of repetition / excessive typing. In an ideal language I'd like to be able to do something like this:

for (i in someVariable) {
    blah(i->x,i->y);
    woop(i->z);
}

It seems like iterating through everything in a sequence would be an incredibly common operation. Is there a way to do it in which the code isn't twice as long as it should have to be?

Upvotes: 5

Views: 3435

Answers (6)

Matt Kennel
Matt Kennel

Reputation: 134

"struct functor {
  void operator()(Foo& arg){
    blah(arg.x, arg.y);
    woop(arg.z);
  }
};

std::for_each(someVariable.begin(), someVariable.end(), functor());"

I think approaches like these are often needlessly baroque for a simple problem.

do i=1,N
 call blah( X(i),Y(i) )
 call woop( Z(i) )
end do

is perfectly clear, even if it's 40 years old (and not C++, obviously).

If the container is always a vector (STL name), I see nothing wrong with an index and nothing wrong with calling that index an integer.

In practice, often one needs to iterate over multiple containers of the same size simultaneously and peel off a datum from each, and do something with the lot of them. In that situation, especially, why not use the index?

As far as SSS's points #2 and #3 above, I'd say it could be so for complex cases, but often iterating 1...N is often as simple and clear as anything else.

If you had to explain the algorithm on the whiteboard, could you do it faster with, or without, using 'i'? I think if your meatspace explanation is clearer with the index, use it in codespace.

Save the heavy C++ firepower for the hard targets.

Upvotes: 0

Satbir
Satbir

Reputation: 6506

Prefer algorithm calls to hand-written loops

There are three reasons:

1) Efficiency: Algorithms are often more efficient than the loops programmers produce

2) Correctness: Writing loops is more subject to errors than is calling algorithms.

3) Maintainability: Algorithm calls often yield code that is clearer and more
straightforward than the corresponding explicit loops.

Upvotes: 1

Sergey Podobry
Sergey Podobry

Reputation: 7189

By the way, MSVS 2008 has a "for each" C++ keyword. Look at How to: Iterate Over STL Collection with for each.

int main() {
   int retval = 0;

   vector<int> col(3);
   col[0] = 10;
   col[1] = 20;
   col[2] = 30;

   for each( const int& c in col )
      retval += c;

   cout << "retval: " << retval << endl;
}

Upvotes: 1

Jerry Coffin
Jerry Coffin

Reputation: 490108

Prefer almost every other algorithm to for_each()

There are two reasons:

  1. for_each is extremely general, telling you nothing about what's really being done, just that you're doing something to all the items in a sequence.
  2. A more specialized algorithm will often be simpler and more direct

Consider, an example from an earlier reply:

void print(int v)
{
    std::cout << v << std::endl;
}
// ...
std::for_each(array.begin(), array.end(), print); // using STL

Using std::copy instead, that whole thing turns into:

std::copy(array.begin(), array.end(), std::ostream_iterator(std::cout, "\n"));

Upvotes: 0

Stack Overflow is garbage
Stack Overflow is garbage

Reputation: 247919

Not counting BOOST_FOREACH which AraK already suggested, you have the following two options in C++ today:

void function(Foo& arg){
  blah(arg.x, arg.y);
  woop(arg.z);
}

std::for_each(someVariable.begin(), someVariable.end(), function); 

struct functor {
  void operator()(Foo& arg){
    blah(arg.x, arg.y);
    woop(arg.z);
  }
};

std::for_each(someVariable.begin(), someVariable.end(), functor());

Both require you to specify the "body" of the loop elsewhere, either as a function or as a functor (a class which overloads operator()). That might be a good thing (if you need to do the same thing in multiple loops, you only have to define the function once), but it can be a bit tedious too. The function version may be a bit less efficient, because the compiler is generally unable to inline the function call. (A function pointer is passed as the third argument, and the compiler has to do some more detailed analysis to determine which function it points to)

The functor version is basically zero overhead. Because an object of type functor is passed to for_each, the compiler knows exactly which function to call: functor::operator(), and so it can be trivially inlined and will be just as efficient as your original loop.

C++0x will introduce lambda expressions which make a third form possible.

std::for_each(someVariable.begin(), someVariable.end(), [](Foo& arg){
  blah(arg.x, arg.y);
  woop(arg.z);
});

Finally, it will also introduce a range-based for loop:

for(Foo& arg : my_someVariable)
{
  blah(arg.x, arg.y);
  woop(arg.z);
}

So if you've got access to a compiler which supports subsets of C++0x, you might be able to use one or both of the last forms. Otherwise, the idiomatic solution (without using Boost) is to use for_eachlike in one of the two first examples.

Upvotes: 5

Khaled Alshaya
Khaled Alshaya

Reputation: 96859

You could use for_each from the standard library. You could pass a functor or a function to it. The solution I like is BOOST_FOREACH, which is just like foreach in other languages. C+0x is gonna have one btw.

For example:

#include <iostream>
#include <vector>
#include <algorithm>
#include <boost/foreach.hpp>

#define foreach BOOST_FOREACH 

void print(int v)
{
    std::cout << v << std::endl;
}

int main()
{
    std::vector<int> array;

    for(int i = 0; i < 100; ++i)
    {
        array.push_back(i);
    }

    std::for_each(array.begin(), array.end(), print); // using STL

    foreach(int v, array) // using Boost
    {
        std::cout << v << std::endl;
    }
}

Upvotes: 12

Related Questions