10

In §25.2.4.2 of the C++ standard (std::for_each):

template<class InputIterator, class Function>   Function
for_each(InputIterator first, InputIterator last, Function f);

Effects: Applies f to the result of dereferencing every iterator in the range [first,last), starting from first and proceeding to last - 1.

  • Does this mean that f is applied to the elements of a container in order?
  • If so, does the parallel mode of libstdc++ violate it?
  • If not, why is the range-based for loop in §6.5.4 not implemented as a call to std::for_each? (this would allow range-based for loops to also be automatically parallelized by the implementation)
gnzlbg
  • 7,135
  • 5
  • 53
  • 106

2 Answers2

8
  • Does this mean that f is applied to the elements of a container in order?

I originally said no, but I think it does mean that, yes. Other algorithms don't include that specific wording.

  • If so, does the parallel mode of libstdc++ violate it?

Maybe, the parallel mode is an extension, and somewhat experimental, not really claiming to be a 100% conforming implementation of the standard library. (If it does claim that somewhere in the docs I'll fix the docs! ;-)

  • If not, why is the range-based for loop in §6.5.4 not implemented as a call to std::for_each? (this would allow range-based for loops to also be automatically parallelized)

Range-based for does not depend on the standard library to work. If std::begin and std::end are visible they might be used, but are not required. Also, it would involve packaging up the loop body as a lambda so you have a function object to pass to std::for_each, which would complicate the specification of range-based for, which is supposed to have the same semantics and be exactly as efficient as a hand-written for loop. But the real reason might be that noone thought to do it that way!

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
  • Some of us were having a similar discussion about `std::generate` which led to [this question](http://stackoverflow.com/questions/14823732/c-standard-wording-does-through-all-iterators-in-the-range-imply-sequential). I suspect you may be in a position to shed some light on this :-) – juanchopanza Feb 12 '13 at 18:02
  • I'm afraid I have no particular insight about sequential guarantees ... it's something I keep meaning to look into – Jonathan Wakely Feb 12 '13 at 18:10
  • I've always written `std::for_each` because I thought it was more ''parallelization friendly'' than a for-loop (just replace it with a `lib::parallel_for_each`, or maybe just compile with openMP and let the compiler choose a parallel implementation of it for you). This is keeping me from switching to the more confortable range-based for loops. I thought that maybe they would be implemented as just a call to `std::for_each`, sad to hear they aren't :( – gnzlbg Feb 12 '13 at 18:22
  • 3
    Another difference is when aborting mid-sequence: for_each can only do so by throwing an exception, while range-based for can also do so with a break or return statement. – Nevin Feb 12 '13 at 18:27
  • 1
    @Nevin I personally don't like throwing an exception inside for each, the algorithm literally means `for each element in the range`. Throwing an exception changes its meaning to `for each element in the range, or not.` If you want to break, just let the lambda return for the remaining elements if a condition is met. That way you still traverse the sequence till the end. This maps good to the concept of parallel for eachs: OpenMP (and others) doesn't allow you to break out of loops either. – gnzlbg Feb 19 '13 at 23:27
3

If not, why is the range-based for loop in §6.5.4 not implemented as a call to std::for_each? (this would allow range-based for loops to also be automatically parallelized)

Well, std::for_each is not allowed by the standard to be "automatically parallelized" (it must proceed sequentially, as stated in the standard), so there's that. But more importantly, range-based for allows other things besides std::for_each. As a language feature, you get to, for example, break out of the loop. You can use goto or other language constructs. And so forth.

std::for_each is based around calling a function for each iteration. And you can't really "break" out of a function.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • Thanks! Yes you can break out of a range-based for. Does this mean that std::for_each should still be preferred if you think about future parallelization? You can't break out of a parallel openmp loop so actually std::for_each maps better with openmp than the range-based for. – gnzlbg Feb 12 '13 at 23:03
  • @gnzlbg: No. `std::for_each` is not allowed to be parallelized. There are algorithms *like* it that can be, but `std::for_each` isn't for that. If you're doing a loop that you want to be done in parallel, you should use an appropriate algorithm for that. `std::for_each` is not that algorithm. – Nicol Bolas Feb 13 '13 at 01:00
  • thanks! After reading the answers now I know that `std::for_each` is not allowed to be parallelized. However, I was asking now if in the case I might be thinking of parallelizing a loop in the future, should I still prefer `std::for_each` over range-based for loops because its requirements map better with those of parallel loops? Changing `std::for_each` to `my::parallel_for_each` should be easier than moving away from a range-based for loop. – gnzlbg Feb 13 '13 at 09:16