I want to use std::transform
with a parallel execution policy. The documentation tells to use the template (2):
template< class ExecutionPolicy,
class ForwardIt1,
class ForwardIt2,
class UnaryOperation >
ForwardIt2 transform( ExecutionPolicy&& policy,
ForwardIt1 first1,
ForwardIt1 last1,
ForwardIt2 d_first,
UnaryOperation unary_op );
My code is as follows, and seems to match the template:
const std::vector<SimulatedBody>& bodies = m_data.back().m_bodies;
std::vector<SimulatedBody> updated_bodies;
std::transform(std::execution::par,
bodies.begin(),
bodies.end(),
std::back_inserter(updated_bodies),
[&](const SimulatedBody& body) {
return body.updated(*quadtree, m_dt, m_force_algorithm_fn);
});
The return type of body.updated
is a new SimulatedBody
:
SimulatedBody SimulatedBody::updated(
const bh::Node& quadtree, float dt,
const std::function<Vector2d(const Node&, const Body&)>&
force_algorithm_fn = bh::compute_approximate_net_force_on_body) const
However, when I compile, it raises the following error:
/usr/bin/../lib/gcc/x86_64-redhat-linux/12/../../../../include/c /12/pstl/glue_algorithm_impl.h:325:44: error: argument may not have 'void' type
[__op](_InputType __x, _OutputType __y) mutable { __y = __op(__x); },
^
/home/steddy/CLionProjects/barnes-hut/src/simulation/simple_simulator.cpp:35:8: note: in instantiation of function template specialization 'std::transform<const __pstl::execution::parallel_policy &, __gnu_cxx::__normal_iterator<const bh::SimulatedBody *, std::vector<bh::SimulatedBody>>, std::back_insert_iterator<std::vector<bh::SimulatedBody>>, (lambda at /home/steddy/CLionProjects/barnes-hut/src/simulation/simple_simulator.cpp:37:18)>' requested here
std::transform(std::execution::par, bodies.begin(), bodies.end(),
^
From the error, it seems that the compiler it is trying to use the wrong template; in particular, one with a BinaryOperation.
Am I missing something?
I am using gcc version 12.1.1 20220507 (Red Hat 12.1.1-1).
CodePudding user response:
My code [...] seems to match the template:
It doesn't:
template< class ExecutionPolicy,
class ForwardIt1,
class ForwardIt2,
class UnaryOperation >
ForwardIt2 transform( ExecutionPolicy&& policy,
ForwardIt1 first1,
ForwardIt1 last1,
ForwardIt2 d_first, // < here
UnaryOperation unary_op );
In the Parameters section, the documentation you linked specifies the beginning of the destination range, parameter d_first
, should be a forward iterator:
Type requirements
- [...]
ForwardIt1
,ForwardIt2
,ForwardIt3
must meet the requirements of LegacyForwardIterator.
You're passing it an output iterator, i.e.:
std::back_inserter(updated_bodies)
If SimulatedBody
is default constructible, you may resize updated_bodies
to the size of bodies
and pass updated_bodies.begin()
(which is a random-access iterator, hence a forward iterator too) to std::transform
:
const std::vector<SimulatedBody>& bodies = m_data.back().m_bodies;
std::vector<SimulatedBody> updated_bodies;
updated_bodies.resize(bodies.size()); // Resize output vector
std::transform(std::execution::par,
bodies.begin(),
bodies.end(),
updated_bodies.begin(), // Pass iterator to first element
[&](const SimulatedBody& body) {
return body.updated(*quadtree, m_dt, m_force_algorithm_fn);
});