Please consider the follownig simple code snippet:
template<typename T>
struct point2{ T x, y; };
template<typename T>
std::complex<T> foo(std::vector<point2<T>> const& x)
{
std::reduce(std::execution::par_unseq, x.begin(), x.end(), std::complex<T>{},
[&](std::complex<T> const& first, point2<T> const& second) {
return first std::complex<T>{ second.x, second.y };
});
}
Using Visual Studio 2022 (with C -Language set to C 20), I'm receiving the error message
'int foo::<lambda_1>::operator ()(const std::complex &,const point2 &) const': cannot convert argument 2 from 'std::complex' to 'const point2 &'
What is going wrong here? It seems like the type to which the iterators in the definition of std::reduce
point to need to be the same as the chosen init value. But this doesn't seem to be a requirement described in the C standard. Am I missing something?
CodePudding user response:
std::reduce
allows elements in the set to be grouped and rearranged in any order to allow for more efficient implementations. It is required that all combinations of *first
and init
are valid arguments to the binary_op
(quote from 27.10.4 Reduce #5):
Mandates: All of
binary_op(init, *first), binary_op(*first, init), binary_op(init, init), and binary_op(*first, *first)
are convertible to T.
You seem to be looking for std::accumulate
, which has the same effects, but ensures that order of evaluation is kept.
To keep possibility of using std::execution::par_unseq
, you may try to add conversions between point2
and std::complex
:
template<typename T>
struct point2{
T x, y;
point2(const std::complex<T>& c): x{c.real()}, y{c.imag()} {}
// Or replace first argument in lambda with point2<T>
// instead of this conversion operator, and adjust the rest accordingly
operator std::complex<T>() { return {x, y};}
};
Altough I'm not sure if the conversions won't take away the benefit of parallelizing the algorithm, you should test and profile both versions before using them.