Home > Software engineering >  Why does the compiler try to instantiate the wrong STL template? (BinaryOperation instead of UnaryOp
Why does the compiler try to instantiate the wrong STL template? (BinaryOperation instead of UnaryOp

Time:05-31

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);
    });
  • Related