Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is function passed to std::for_each allowed to make copies of sequence elements?

I've recently stumbled upon this wording from cppreference:

Unlike the rest of the algorithms, for_each is not allowed to make copies of the elements in the sequence even if they are trivially copyable.

Is the statement correct? I haven't found any grounds in the standard. Do I understand well, that it would btw. imply the following example from the same page invalid?

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

int main()
{
    std::vector<int> nums{3, 4, 2, 8, 15, 267};
 
    // ...
 
    // calls Sum::operator() for each number
    Sum s = std::for_each(nums.begin(), nums.end(), Sum());
 
    // ...
}
like image 545
neonxc Avatar asked Oct 05 '17 21:10

neonxc


2 Answers

That sentence on cppreference.com comes from the wording added to C++17 about the new support for parallel algorithms.

[algorithms.parallel.exec]/3 says:

Unless otherwise stated, implementations may make arbitrary copies of elements (with type T) from sequences where is_trivially_copy_constructible_v<T> and is_­trivially_­destructible_­v<T> are true.

Note this is in a context discussing parallel algorithms, defined as standard library function templates which have a template parameter named ExecutionPolicy.

But then [alg.foreach]/9 says of for_each(ExecutionPolicy&&, ForwardIterator, ForwardIterator, Function):

Implementations do not have the freedom granted under [algorithms.parallel.exec] to make arbitrary copies of elements from the input sequence.

Presumably some parallelization techniques can be more efficient for trivial types by making copies of the elements. (Maybe to make them contiguous in memory? I'm just guessing.)

So none of this applies to the older non-parallel for_each(InputIterator first, InputIterator last, Function f). For that algorithm, it's simply the case that since the effects are specified as "Applies f to the result of dereferencing every iterator in the range [first, last)...", the functor argument must be e.g. *first and not a copy of *first.

like image 162
aschepler Avatar answered Oct 26 '22 08:10

aschepler


The wording is indeed confusing, as it is also mentionned that

The signature of the function should be equivalent to the following:
void fun(const Type &a);
The signature does not need to have const &.

I think you need to see the fact that for_each is not allowed to make copies of the elements as a guarantee given by the standard to the user rather than a limitation on the user predicate function applied to each element..

like image 23
UmNyobe Avatar answered Oct 26 '22 07:10

UmNyobe