Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to have a auto for loops, alternative to nested for loops?

I am wondering if there is something available in C++ which helps us to iterate over the two nested loops while using auto. Say like, I would want to compare an array element with all other elements forward. This is how we do it traditionally:

std::vector<int> vec {1, 2, 3, 4};

for (int i = 0; i < vec.size(); ++i) 
{
    for (int j = i + 1; j < vec.size(); ++j) 
    {
        if (vec[i] == vec[j]) {}
        // Compares 1 with 2, 3 and 4
        // Compares 2 with 3 and 4
    }
}

The intention is to use auto in order to achieve this.

std::vector<int> vec{1, 2, 3, 4};

for (<auto>& i : vec)
//   ^^^^^^^
{
    // What should I write here so that I compare only the forward elements?
}

We can probably use something like this:

for (auto it = vec.begin(); it != vec.end(); ++it)
{
    for (auto jt = it + 1; jt != vec.end(); ++jt) 
    {
        // Do a comparison here.
    }
}

And the third snapshot again writes more code. I am looking to get more insights on the plain second snapshot.

Mentioned in the question itself.

like image 359
Hemant Bhargava Avatar asked Oct 20 '25 03:10

Hemant Bhargava


2 Answers

I am wondering if there is something available in C++ which helps us to iterate over the two nested loops while using auto.

Not exactly what you wished for; However, with std::ranges::iota_view (Since c++20), you could write nested range-based for loops like follows:

#include <ranges> // std::ranges::iota_view

for (const auto i : std::views::iota(0u, std::size(vec)))
{
    for (const auto j : std::views::iota(i + 1u, std::size(vec)))
    {
        if (vec[i] == vec[j])
        {
            // .....
        }
    }
}

See a live demo in godbolt.org

Additionally, this enables you to have i and j be const over the loop scope.


Side note: For compilers older than C++20, one could easily implement an iterator that act exactly like std::ranges::iota_view.

like image 67
JeJo Avatar answered Oct 22 '25 16:10

JeJo


In c++20, something like this, perhaps?

#include <span>

/*...*/ 

std::size_t offset = 0;

for (auto &i: vec) 
{
    for (auto &j: std::span(vec).subspan(++offset)) 
    {
        if (i == j) {}
        // Compares 1 with 2, 3 and 4
        // Compares 2 with 3 and 4           
    }
}

offset is guaranteed to be always <= vec.size(), hence subspan() is well defined.

If you don't want to introduce a new variable, this would also work, but might produce more verbose assembly and look quite unusual:

for (auto &i: vec) 
{
    for (auto &j: std::span(vec).subspan(&i - vec.data() + 1)) 
    {
        if (i == j) {}
        // Compares 1 with 2, 3 and 4
        // Compares 2 with 3 and 4           
    }
}

For the shortest assembly, having the outer loop iterate over a span already is the best, so that the span doesn't have to be constructed at each outer iteration.

std::span span(vec);
std::size_t offset = 0;

for (auto &i: span) 
{
    for (auto &j: span.subspan(++offset)) 
    {
        if (i == j) {}
        // Compares 1 with 2, 3 and 4
        // Compares 2 with 3 and 4           
    }
}
like image 32
Fabio A. Avatar answered Oct 22 '25 16:10

Fabio A.