Search code examples
c++for-loopc++11foreachnested-loops

Iterate over all the other objects excluding the current one


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.


Solution

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