Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does ranges::for_each return the function?

The legacy std::for_each returns function as the standard only requires Function to meet Cpp17MoveConstructible according to [alg.foreach]:

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

Preconditions: Function meets the Cpp17MoveConstructible requirements.

[Note: Function need not meet the requirements of Cpp17CopyConstructible. end note]

This is reasonable since the user may want to reuse the function after the call.

The parallel version of for_each has no return:

template<class ExecutionPolicy, class ForwardIterator, class Function>
  void for_each(ExecutionPolicy&& exec,
                ForwardIterator first, ForwardIterator last,
                Function f);

Preconditions: Function meets the Cpp17CopyConstructible requirements.

This is because the standard requires Function to meet the Cpp17CopyConstructible, so returning the function is unnecessary as the user can freely create a copy if they want on the call side.

I noticed that ranges::for_each also returns the function:

template<input_iterator I, sentinel_for<I> S, class Proj = identity,
         indirectly_unary_invocable<projected<I, Proj>> Fun>
  constexpr ranges::for_each_result<I, Fun>
    ranges::for_each(I first, S last, Fun f, Proj proj = {});

However, the function signature already requires Fun to satisfy indirectly_unary_invocable which already guarantees that it is copy constructible.

The question is, why does the ranges::for_each still return the function? What's the point of doing this?

like image 488
康桓瑋 Avatar asked Nov 16 '25 02:11

康桓瑋


1 Answers

It returns the functor because that allowed some clever tricks with stateful functors back in the day (in C++98, I assume). You don't see those often today, because lambdas are usually more straightforward.

Here's an example:

#include <algorithm>
#include <iostream>

struct EvenCounter
{
    int count;

    EvenCounter() : count(0) {}

    void operator()(int x)
    {
        if (x % 2 == 0)
            count++;
    }
};

int main()
{
    int array[] = {1,2,3,4,5};
    int num_even = std::for_each(array, array+5, EvenCounter()).count;
    std::cout << num_even << '\n';
}

This is reasonable since the user may want to reuse the function after the call.

I think the logic is backwards here. The function isn't required to be copyable simply because there's no reason for for_each to copy it.

If you have a non-copyable (or even non-movable) function, you can pass it by reference using std::ref to avoid copies/moves, so you don't win anything here from the algorithm returning the function back to you.

There was no std::ref in C++98, but there was also no move semantics, so for_each couldn't have worked with non-copyable functors in the first place.

like image 108
HolyBlackCat Avatar answered Nov 18 '25 17:11

HolyBlackCat



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!