5

For a very simple thing, like for example to print each element in a vector, what is the better way to use in C++?

I have been using this:

for (vector<int>::iterator i = values.begin(); i != values.end(); ++i)

before, but in one of the Boost::filesystem examples I have seen this way:

for (vec::const_iterator it(v.begin()), it_end(v.end()); it != it_end; ++it)

For me it looks more complicated and I don't understand why is it better then the one I have been using.

Can you tell me why is this version better? Or it doesn't matter for simple things like printing elements of a vector?

Does i != values.end() make the iterating slower?

Or is it const_iterator vs iterator? Is const_iterator faster in a loop like this?

Ajay
  • 18,086
  • 12
  • 59
  • 105
hyperknot
  • 13,454
  • 24
  • 98
  • 153
  • 2
    Whether they are faster or not, be aware that `const_iterator` has different semantics to `iterator` – Mike Caron Jul 12 '11 at 22:40
  • Tony gave the correct answer to this one. But I suggest you use simple version having full syntax, but use const_iterator. Then move to typedefing the container, then the iterator itself. Having 'it_end' is a matter of choice. Later, if using C++0x, you may like (love) to use auto! – Ajay Jul 13 '11 at 15:01

5 Answers5

10
  1. Foo x = y; and Foo x(y); are equivalent, so use whichever you prefer.

  2. Hoisting the end out of the loop may or may not be something the compiler would do anyway, in any event, it makes it explicit that the container end isn't changing.

  3. Use const-iterators if you aren't going to modify the elements, because that's what they mean.

for (MyVec::const_iterator it = v.begin(), end = v.end(); it != end; ++it)
{
  /* ... */
}

In C++0x, use auto+cbegin():

for (auto it = v.cbegin(), end = v.cend(); it != end; ++it)

(Perhaps you'd like to use a ready-made container pretty-printer?)

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • I like the simplicity of the auto + cbegin(). Just a quick question is it an alternative of const_iterator or iterator? I mean is using auto + cbegin() read only? – hyperknot Jul 12 '11 at 22:49
  • 2
    `cbegin()` returns a const_iterator, so you can use it with `auto`. If you say `auto it = v.begin()` you get a non-const iterator. – Kerrek SB Jul 12 '11 at 22:50
6
for (vector<int>::iterator i = values.begin(); i != values.end(); ++i)

...vs...

for (vec::const_iterator it(v.begin()), it_end(v.end()); it != it_end; ++it)

For me [the latter, seen in boost] looks more complicated and I don't understand why is it better then the one I have been using.

I'd say it would look more complicated to anybody who hasn't got some specific reason for liking the latter to the extent that it distorts perception. But let's move on to why it might be better....

Can you tell me why is this version better? Or it doesn't matter for simple things like printing elements of a vector? Does i != values.end() make the iterating slower?

  • it_end

    • Performance: it_end gets the end() value just once as the start of the loop. For any container where calculating end() was vaguely expensive, calling it only once may save CPU time. For any halfway decent real-world C++ Standard library, all the end() functions perform no calculations and can be inlined for equivalent performance. In practice, unless there's some chance you may need to drop in a non-Standard container that's got a more expensive end() function, there's no benefit to explicitly "caching" end() in optimised code.

      This is interesting, as it means for vector that size() may require a small calculation - conceptually subtracting begin() from end() then dividing by sizeof(value_type) (compilers scale by size implicitly during pointer arithmetic), e.g. GCC 4.5.2:

      size_type size() const
      { return size_type(this->_M_impl._M_finish - this->_M_impl._M_start); }

    • Maintenance: if the code evolves to insert or erase elements inside the loop (obvious in such a way that the iterator itself isn't invalidated - plausible for maps / sets / lists etc.) it's one more point of maintenance (and hence error-proneness) if the cached end() value also needs to be explicitly recalculated.

  • A small detail, but here vec must be a typedef, and IMHO it's often best to use typedefs for containers as it loosens the coupling of container type with access to the iterator types.

  • type identifier(expr)

    • Style and documentary emphasis: type identifier(expr) is more directly indicative of a constructor call than type identifier = expr, which is the main reason some people prefer the form. I generally prefer the latter, as I like to emphasise the sense of assignment... it's visually unambiguous whereas function call notation is used for many things.

    • Near equivalence: For most classes, both invoke the same constructor anyway, but if type has an explicit constructor from the type of expr, it will be passed over if = is used. Worse still, some other conversion may allow a less ideal constructor be used instead. For example, X x = 3.14;, would pass over explicit X::X(double); to match X::X(int) - you could get a less precise (or just plain wrong) result - but I'm yet to be bitten by such an issue so it's pretty theoretical!

Or is it const_iterator vs iterator? Is const_iterator faster in a loop like this?

For Standard containers, const_iterator and iterator perform identically, but the latter implies you want the ability to modify the elements as you iterate. Using const_iterator documents that you don't intend to do that, and the compiler will catch any contradictory uses of the iterator that attempt modification. For example, you won't be able to accidentally increment the value the iterator addresses when you intend to increment the iterator itself.

Given C++0x has been mentioned in other answers - but only the incremental benefit of auto and cbegin/cend - there's also a new notation supported:

for (const Foo& foo: container)
    // use foo...
Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
  • Great answer... Although you actually convinced me `Foo x(y)` is preferable to `Foo x = y`. (a) It "emphasizes the sense of assignment", but it is _not_ assignment, so that is a bug, not a feature; and (b) subtle potential bugs that I never even knew about. (In particular, the meaning of the code can change just because somebody adds an `explicit` keyword someplace far away. Yuck.) – Nemo Jul 13 '11 at 04:55
  • @Nemo: all fair enough, though for me "assigning an initial value" is just a *concept* that I find comfortable and easily keep distinct from the more concrete operation of the `=` operator. The bug potential is a nasty one... I only thought about that one myself as I was drafting the answer, then verified it in code :-). Cheers, Tony – Tony Delroy Jul 13 '11 at 05:18
3

To print the items in a vector, you shouldn't be using any of the above (at least IMO).

I'd recommend something like this:

std::copy(values.begin(), values.end(), 
          std::ostream_iterator<T>(std::cout, "\n"));
Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • +1 sound advice where applicable; the pain is that you can't use it directly to e.g. comma-separate the elements as it puts a trailing comma on too.... – Tony Delroy Jul 13 '11 at 05:32
  • @Tony: You just need to use an [infix_ostream_iterator](http://stackoverflow.com/questions/3496982/printing-lists-with-commas-c/3497021#3497021) for that. – Jerry Coffin Jul 13 '11 at 06:41
0

You could just access them by index

int main(int argc, char* argv[])
{
    std::vector<int> test;

    test.push_back(10);
    test.push_back(11);
    test.push_back(12);

    for(int i = 0; i < test.size(); i++)
        printf("%d\n", test[i]);
}

prints out: 10 11 12

Drake
  • 3,851
  • 8
  • 39
  • 48
  • 3
    That's a very un-C++ suggestion! Boo :-S – Kerrek SB Jul 12 '11 at 22:44
  • 2
    @kerrek true but there's also less syntactic noise. – greatwolf Jul 12 '11 at 22:51
  • There should be a general preference for a familiar cross-language notation: it's popular because it's intuitive and sufficient in most cases. Beginners shouldn't have to feel like there's a C++-specific way they *have* to use to do all the tasks they're used to: it's unnecessary discouragement as they learn C++ after other languages or work simultaneously in many languages. C++0x recognises this with the new `for` notation. That said, iterating doesn't depend on the container having array-style `operator[]` semantics, so can be used for `lists`, `maps`, `unordered_maps` etc.. – Tony Delroy Jul 13 '11 at 02:35
0

I don't think it matters. Internally, they do the same thing, so you compiler should optimise it anyway. I would personally use the first version as I find it much clearer as it closely follows the for-loop strucutre.

for (vector<int>::iterator i = values.begin(); i != values.end(); ++i)
Chris
  • 1,501
  • 17
  • 32