I am a C++ newbie and am developing a C++17 application.
I want to iterate a vector of elements, and, for each element, produce a new version of that element and store the result into another vector. I want to do this in parallel. To do this, I can use std::transform
. My code looks as follows:
std::vector<SimulatedBody> updated_bodies;
updated_bodies.reserve(bodies.size());
std::transform(std::execution::par_unseq, // make the loop parallel
bodies.begin(),
bodies.end(),
updated_bodies.begin(),
[&](const SimulatedBody &body) {
auto new_body = body.updated(*quadtree, m_dt);
// new_body is correctly created, I can step here with a debugger
return new_body;
});
std:transform
requires the unary operation function object to be applied to have a signature equivalent to the following:
Ret fun(const Type &a);
The type Type must be such that an object of type InputIt can be dereferenced and then implicitly converted to Type. The type Ret must be such that an object of type OutputIt can be dereferenced and assigned a value of type Ret.
Moreover, since that I am using the parallel execution policy, it requires the iterator of the output vector to be a forward iterator (therefore, I can't use back_inserter
).
I think that my code meets these requirements. In particular, in my case, Ret
is a SimulatedBody
; OutputIt
is a SimulatedBody
too; and SimulatedBody
has both a copy assignment operator and a move assignment operator. (I point out that SimulatedBody
inerits from Body
).
#ifdef DEBUG_MOVE_ASSIGNMENT_OPERATOR
SimulatedBody &SimulatedBody::operator=(SimulatedBody &&other) noexcept {
Body::operator=(std::move(other));
std::cout << "SimulatedBody move assignment operator\n";
m_velocity = std::move(other.m_velocity); // NOLINT(bugprone-use-after-move)
return *this;
}
#else
SimulatedBody &SimulatedBody::operator=(SimulatedBody &&other) noexcept =
default;
#endif
#ifdef DEBUG_MOVE_ASSIGNMENT_OPERATOR
Body &Body::operator=(Body &&other) noexcept {
std::cout << "Body move assignment operator\n";
m_position = std::move(m_position);
m_mass = other.m_mass;
return *this;
}
#else
Body &Body::operator=(Body &&other) noexcept = default;
#endif
When the transform
code executes, it prints that the new_body
ies are moved into the updated_bodies
vector's range:
Body move assignment operator
SimulatedBody move assignment operator
Body move assignment operator
SimulatedBody move assignment operator
Body move assignment operator
SimulatedBody move assignment operator
...
However, to my surprise, at the end of the transform
, updated_bodies
is empty: I expected updated_bodies
to contain the same number of elements of bodies
.
Why is this the case?
- Is it because I called
updated_bodies.reserve()
? I think this makes sense; I need to preallocate some memory in the output vector to host the transformed elements. - Does the move assignment operator have issues I can't see?