std::for_each(3)

πŸ“¦ version 2024.07.05, in nixpkgs unstable, opensuse-leap 16.0

✨ NAME ✨

std::for_each β€” literally just std::for_each, bestie. No cap, that's the whole name. It's giving "what it says on the tin."


🌸 SYNOPSIS 🌸

Defined in header <algorithm>:

template< class InputIt, class UnaryFunc >
UnaryFunc for_each( InputIt first, InputIt last,         (1)
(constexpr since C++20)
UnaryFunc f );
template< class ExecutionPolicy, class ForwardIt, class UnaryFunc >

void for_each( ExecutionPolicy&& policy,                 (2) (since C++17)

               ForwardIt first, ForwardIt last,
UnaryFunc f );

Okay so real talk: this algorithm applies the given function object f to the result of dereferencing every iterator in the range [first, last). If f returns a result, the result is ignored. Like, we don't care about your return value here, bestie. It's giving "do the thing and move on."

1) f is applied in order starting from first.

If UnaryFunc is not MoveConstructible, the behavior is undefined. (since C++11)

2) f might not be applied in order. The algorithm is executed according to policy.

This overload participates in overload resolution only if:

  • std::is_execution_policy_v<std::decay_t<ExecutionPolicy>> is true. (until C++20)
  • std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>> is true. (since C++20)

If UnaryFunc is not CopyConstructible, the behavior is undefined.

If the iterator type (InputIt/ForwardIt) is mutable, f may modify the elements of the range through the dereferenced iterator.

Important vibe check: Unlike the rest of the parallel algorithms, for_each is not allowed to make copies of the elements in the sequence even if they are TriviallyCopyable. Periodt. That's just how it is.

πŸŽ€ PARAMETERS πŸŽ€

  • first, last β€” the range to apply the function to. Like, the whole squad of elements you're about to hit with f.
  • policy β€” the execution policy to use. See execution policy for details. (iykyk)
  • f β€” function object, to be applied to the result of dereferencing every iterator in the range [first, last). The signature of the function should be equivalent to the following:
void fun(const Type &a);

The signature does not need to have const &. The type Type must be such that an object of type InputIt can be dereferenced and then implicitly converted to Type.


πŸ¦‹ TYPE REQUIREMENTS πŸ¦‹

  • InputIt must meet the requirements of LegacyInputIterator.
  • ForwardIt must meet the requirements of LegacyForwardIterator.

πŸ’… RETURN VALUE πŸ’…

  1. f β€” yes, it returns your function object back to you. Like, "here bestie, you dropped this."
  2. (none) β€” for the parallel overload, it's giving "nothing to see here."

⭐ COMPLEXITY ⭐

Exactly std::distance(first, last) applications of f. No more, no less. It's giving "precise queen."


πŸ”₯ EXCEPTIONS πŸ”₯

The overload with a template parameter named ExecutionPolicy reports errors as follows:

  • If execution of a function invoked as part of the algorithm throws an exception and ExecutionPolicy is one of the standard policies, std::terminate is called. For any other ExecutionPolicy, the behavior is implementation-defined.
  • If the algorithm fails to allocate memory, std::bad_alloc is thrown.
Warning bestie: std::terminate is not a vibe. It literally kills your program. So like, don't let your function throw with standard policies, okay? 🫢

🌈 POSSIBLE IMPLEMENTATION 🌈

See also the implementations in libstdc++, libc++ and MSVC stdlib.

template<class InputIt, class UnaryFunc>
constexpr UnaryFunc for_each(InputIt first, InputIt last, UnaryFunc f)
{
    for (; first != last; ++first)
        f(*first);

    return f; // implicit move since C++11
}

Like, that's it. That's the whole algorithm. So simple, so iconic.


πŸ’œ NOTES πŸ’œ

For overload (1), f can be a stateful function object. The return value can be considered as the final state of the batch operation. Like, you can accumulate state in your functor and get it back at the end. Main character energy.

For overload (2), multiple copies of f may be created to perform parallel invocation. No value is returned because parallelization often does not permit efficient state accumulation. It's giving "we tried but the vibes were off."


πŸ“ EXAMPLE πŸ“

The following example uses a lambda-expression to increment all of the elements of a vector and then uses an overloaded operator() in a function object (a.k.a., "functor") to compute their sum. Note that to compute the sum, it is recommended to use the dedicated algorithm std::accumulate.

// Run this code

#include <algorithm>
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> v{3, -4, 2, -8, 15, 267};

    auto print = [](const int& n) { std::cout << n << ' '; };

    std::cout << "before:\t";
    std::for_each(v.cbegin(), v.cend(), print);
    std::cout << '\n';

    // increment elements in-place
    std::for_each(v.begin(), v.end(), [](int &n) { n++; });

    std::cout << "after:\t";
    std::for_each(v.cbegin(), v.cend(), print);
    std::cout << '\n';

    struct Sum
    {
        void operator()(int n) { sum += n; }
        int sum {0};
    };

    // invoke Sum::operator() for each element
    Sum s = std::for_each(v.cbegin(), v.cend(), Sum());
    std::cout << "sum:\t" << s.sum << '\n';
}

Output:

before: 3 -4 2 -8 15 267
after:  4 -3 3 -7 16 268
sum:    281

🧚 DEFECT REPORTS 🧚

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

  • LWG 475 β€” Applied to: C++98. Behavior as published: it was unclear whether f can modify the elements of the sequence being iterated over (for_each is classified as "non-modifying sequence operations"). Correct behavior: made clear (allowed if the iterator type is mutable).
  • LWG 2747 β€” Applied to: C++11. Behavior as published: overload (1) returned std::move(f). Correct behavior: returns f (which implicitly moves).

Like, they really said "let's clarify this" and we love that for us. πŸ’–


πŸ‘‘ SEE ALSO πŸ‘‘

  • transform β€” applies a function to a range of elements, storing results in a destination range. (function template)
  • for_each_n (C++17) β€” applies a function object to the first N elements of a sequence. (function template)
  • ranges::for_each (C++20) β€” applies a function to a range of elements. (niebloid)
  • ranges::for_each_n (C++20) β€” applies a function object to the first N elements of a sequence. (niebloid)
  • range-for loop (C++11) β€” executes loop over range.

So yeah, that's std::for_each. Go forth and apply functions to your ranges, bestie. You got this. πŸ’…βœ¨

This iconic piece of documentation was brought to you by http://cppreference.com, dated 2024.06.10, and the manual page is literally std::for_each(3). Slay. πŸŽ€

πŸ“¦ where this page comes from