In many applications is necessary to compute the mutual interactions between a sequence of objects, that is for each object of the sequence, iterate over all the other objects except/skipping/excluding the current one. That's my current solution to this:
std::vector<Object> objects;
for( auto icurr=objects.begin(); icurr!=objects.end(); ++icurr )
{// Iterate over all the other objects excluding this one
for( auto iother=objects.begin(); iother!=icurr; ++iother )
icurr->interact_with(*iother);
for( auto iother=icurr+1; iother!=objects.end(); ++iother )
icurr->interact_with(*iother);
}
But this doesn't satisfy me because it's cumbersome and above all duplicates the loop body. I don't like the banal alternative either:
for( auto icurr=objects.begin(); icurr!=objects.end(); ++icurr )
{
for( auto iother=objects.begin(); iother!=objects.end(); ++iother )
if(icurr!=iother) icurr->interact_with(*iother);
}
Because has an avoidable test; in that case, I would rather write something like:
for( Object& curr : objects )
{
for( const Object& other : objects.excluded(curr) )
curr.interact_with(other);
}
If it would have the same performance, that would be a major readability gain. Can I take advantage of the latest c++ standard to improve the readability my code? Any advice? Disclaimer: this is a coding style question, an attempt to keep updated on best practices, learn and improve.
With range libraries, you might do something like:
for (const Object& curr : objects) {
for (const Object& other : objects | ranges::filter([&](const Object& obj) { return &obj != curr; }) )
curr.interact_with(other);
}
but that would indeed do extra comparison.
Your first version seems optimal.
With std::span
(C++20) (or reimplemented version), you might do, to avoid duplication, something like:
std::vector<Object> objects;
for (auto it = objects.begin(); it != objects.end(); ++it) {
for (auto range : {std::span{objects.begin(), it}, std::span{it + 1, objects.end()}}) {
for (const auto& other : range) {
it->interact_with(other);
}
}
}