On this page
std::transform_reduce
Defined in header <numeric> |
||
---|---|---|
(1) | ||
|
(since C++17) (until C++20) |
|
|
(since C++20) | |
(2) | ||
|
(since C++17) (until C++20) |
|
|
(since C++20) | |
(3) | ||
|
(since C++17) (until C++20) |
|
|
(since C++20) | |
|
(4) | (since C++17) |
|
(5) | (since C++17) |
|
(6) | (since C++17) |
std::transform_reduce(first1, last1, first2, init, std::plus<>(), std::multiplies<>());
, effectively parallelized version of the default std::inner_product
.
transform
to each pair of elements from the ranges [
first
,
last
)
and the range starting at first2
and reduces the results (possibly permuted and aggregated in unspecified manner) along with the initial value init
over reduce
.
transform
to each element in the range [
first
,
last
)
and reduces the results (possibly permuted and aggregated in unspecified manner) along with the initial value init
over reduce
.
policy
. These overloads do not participate in overload resolution unless
|
(until C++20) |
|
(since C++20) |
The behavior is non-deterministic if reduce
is not associative or not commutative.
The behavior is undefined if reduce
, or transform
modifies any element or invalidates any iterator in the input ranges, including their end iterators.
Parameters
first, last | - | the range of elements to apply the algorithm to |
init | - | the initial value of the generalized sum |
policy | - | the execution policy to use. See execution policy for details. |
reduce | - | binary FunctionObject that will be applied in unspecified order to the results of transform , the results of other reduce and init . |
transform | - | unary or binary FunctionObject that will be applied to each element of the input range(s). The return type must be acceptable as input to reduce . |
Type requirements | ||
-T must meet the requirements of MoveConstructible in order to use overloads (3,6). and the result of the expressions reduce(init, transform(*first)) , reduce(transform(*first), init) , reduce(init, init) , and reduce(transform(*first), transform(*first)) must be convertible to T. |
||
-T must meet the requirements of MoveConstructible in order to use overloads (2,5). and the result of the expressions reduce(init, transform(*first1, *first2)) , reduce(transform(*first1, *first2), init) , reduce(init, init) , and reduce(transform(*first1, *first2), transform(*first1, *first2)) must be convertible to T. |
||
-InputIt must meet the requirements of LegacyInputIterator. |
||
-ForwardIt must meet the requirements of LegacyForwardIterator. |
Return value
init
and transform(*first, *first2)
, transform(*(first + 1), *(first2 + 1))
, ..., over reduce
.
init
and transform(*first)
, transform(*(first + 1))
, ... transform(*(last - 1))
over reduce
,
where generalized sum GSUM(op, a1, ..., aN) is defined as follows:
- if N = 1, a1
- if N > 1, op(GSUM(op, b1, ..., bK), GSUM(op, bM, ..., bN)) where
- b1, ..., bN may be any permutation of a1, ..., aN and
- 1 < K + 1 = M ≤ N
in other words, the results of transform or of reduce may be grouped and arranged in arbitrary order.
Complexity
reduce
and transform
.
transform
and reduce
.
Exceptions
The overloads with a template parameter named ExecutionPolicy
report 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 otherExecutionPolicy
, the behavior is implementation-defined. - If the algorithm fails to allocate memory,
std::bad_alloc
is thrown.
Notes
In the unary-binary overload (3,6), transform
is not applied to init
.
If first == last
or first1 == last1
, init
is returned, unmodified.
Example
transform_reduce
can be used to parallelize std::inner_product
. Some systems may need additional support to get advantages of parallel execution. E.g., on GNU/Linux, the Intel TBB be installed and -ltbb
option be provided to gcc/clang compiler.
#if PARALLEL
#include <execution>
#define PAR std::execution::par,
#else
#define PAR
#endif
#include <algorithm>
#include <functional>
#include <iostream>
#include <iterator>
#include <locale>
#include <numeric>
#include <vector>
// to parallelize non-associate accumulative operation, you'd better choose
// transform_reduce instead of reduce; e.g., a + b * b != b + a * a
void print_sum_squared(long const num)
{
std::cout.imbue(std::locale{"en_US.UTF8"});
std::cout << "num = " << num << '\n';
// create an immutable vector filled with pattern: 1,2,3,4, 1,2,3,4 ...
const std::vector<long> v { [n = num * 4] {
std::vector<long> v;
v.reserve(n);
std::generate_n(std::back_inserter(v), n,
[i = 0]() mutable { return 1 + i++ % 4; });
return v;
}()};
auto squared_sum = [](auto sum, auto val) { return sum + val * val; };
auto sum1 = std::accumulate(v.cbegin(), v.cend(), 0L, squared_sum);
std::cout << "accumulate(): " << sum1 << '\n';
auto sum2 = std::reduce(PAR v.cbegin(), v.cend(), 0L, squared_sum);
std::cout << "reduce(): " << sum2 << '\n';
auto sum3 = std::transform_reduce(PAR v.cbegin(), v.cend(), 0L, std::plus{},
[](auto val) { return val * val; });
std::cout << "transform_reduce(): " << sum3 << "\n\n";
}
int main()
{
print_sum_squared(1);
print_sum_squared(1'000);
print_sum_squared(1'000'000);
}
Possible output:
num = 1
accumulate(): 30
reduce(): 30
transform_reduce(): 30
num = 1,000
accumulate(): 30,000
reduce(): -7,025,681,278,312,630,348
transform_reduce(): 30,000
num = 1,000,000
accumulate(): 30,000,000
reduce(): -5,314,886,882,370,003,032
transform_reduce(): 30,000,000
// Compile-options for parallel execution on POSIX:
// g++ -O2 -std=c++17 -Wall -Wextra -pedantic -DPARALLEL ./example.cpp -ltbb -o tr; ./tr
See also
sums up or folds a range of elements (function template) |
|
applies a function to a range of elements, storing results in a destination range (function template) |
|
(C++17)
|
similar to std::accumulate , except out of order (function template) |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/algorithm/transform_reduce