34
votes

While using std::for_each algorithm how do I break when a certain condition is satisfied?

8
I personally don't like exiting early from for loop. IMO, for loop has to be used only when you need to iterate fully. If you want to break in between,consider other looping constructs.Navaneeth

8 Answers

29
votes

You can use std::any_of (or std::all_of or std::none_of) e.g. like this:

std::vector<int> a;
// ...     
std::all_of(a.begin(), a.end(), [&](int val) { 
  // return false if you want to break, true otherwise
  });

However, this is a wasteful solution (return values are not really used for anything), and you're better off writing you own loop.

17
votes

You can use std::find_if algorithm, which will stop and return the iterator to the first element where the predicate condition applied to returns true. So your predicate should be changed to return a boolean as the continue/break condition.

However, this is a hack, so you can use the algorithms.

Another way is to use BOOST_FOREACH.

16
votes

You can break from the for_each() by throwing an exception from your functor. This is often not a good idea however, and there are alternatives.

You can retain state in your functor. If you detect the 'break' condition, simply set a flag in your functor and then for each subsequent iteration simply return without doing your functor's thing. Obviously this won't stop the iteration, which might be expensive for large collections, but it will at least stop the work from being performed.

If your collection is sorted, you can find() the element that you want to break at, then do for_each from begin() to the element find() returned.

Finally, you can implement a for_each_if(). This will again not stop the iteration but will not evaluate your functor which does the work if the predicate evaluates to false. Here are 2 flavors of for_each_xxx(), one which takes a value and performs the work if operator==() evaluates to true, and another which takes two functors; one which performs a comparison ala find_if(), and the other which performs the work if the comparison operator evaluates to true.

/* ---

    For each
    25.1.1

        template< class InputIterator, class Function, class T>
            Function for_each_equal(InputIterator first, InputIterator last, const T& value, Function f)

        template< class InputIterator, class Function, class Predicate >
            Function for_each_if(InputIterator first, InputIterator last, Predicate pred, Function f)

    Requires:   

        T is of type EqualityComparable (20.1.1) 

    Effects:    

         Applies f to each dereferenced iterator i in the range [first, last) where one of the following conditions hold:

            1:  *i == value
            2:  pred(*i) != false

    Returns:    

        f

    Complexity: 

        At most last - first applications of f

    --- */

    template< class InputIterator, class Function, class Predicate >
    Function for_each_if(InputIterator first, 
                         InputIterator last, 
                         Predicate pred, 
                         Function f)
    {
        for( ; first != last; ++first)
        {
            if( pred(*first) )
                f(*first);
        }
        return f;
    };

    template< class InputIterator, class Function, class T>
    Function for_each_equal(InputIterator first, 
                            InputIterator last, 
                            const T& value, 
                            Function f)
    {
        for( ; first != last; ++first)
        {
            if( *first == value )
                f(*first);
        }
        return f;
    };
8
votes

If you want do some actions while condition is not satisfied, maybe you need change algorithm on something like std::find_if?

5
votes

As already shown by others it is only achievable with workarounds that IMHO obfuscate the code.

So my suggestions is to change the for_each into a regular for loop. This will make it more visible to others that you are using break (and maybe even continue).

3
votes

You can't do it, unless you throw an exception, which is not a good idea because you don't do flow control with exceptions.

Update: apparently Boost has a for_each_if that might help, but you're not using Boost.

1
votes

You throw an exception. Whether or not it's a good idea is sort of a style question, pace @Dan, but may be more of an issue with your design. for_each is intended for a sort of functional-programming style, which implicitly assumes that your function can be applied uniformly across the set. So, if you do need to break, that could be consiered an unusual condition, and therefore worthy of an exception.

The other solution, and a more "functional" solution, is to write your function so that if it shouldn't have an effect on some applications, then write it to have no effect. So, for example, if you had a summing function, have it add 0 in the cases you would have "broken" from.

0
votes

You can use std::find_if instead std::for_each:

    int aaa[]{ 1, 2, 3, 4, 5, 6, 7, 8 };
    std::find_if(aaa, std::next(aaa, sizeof(aaa) / sizeof(int)), [](const auto &i) {
        if (i == 5)
            return true;
        std::cout << i << std::endl;
        return false;
    });

output:
1
2
3
4