Home > Software design >  C iterating and range-for-loop differences
C iterating and range-for-loop differences

Time:08-03

I have a chunk of C code that is supposed to go through a vector and delete reoccurring objects in-place. I completed the task (C1) using an iterator. I continued with the problem and wanted to do it using range-based for loop like the latter (C2). However, I ran into complier errors. Can someone explain why these two codes do not function the same and if there is a way of accessing the range-for-loop's internal iterator. C1)

for(auto itr = nums.begin(); itr != nums.end(); itr  ) {
    if(*itr != set) {
        set = *itr;
    } else {
        nums.erase(itr--);
    }
}

C2)

for(auto num : nums) {
    if(num != set) {
        set = num;
    } else {
        nums.erase((&num));
    }
}

CodePudding user response:

Can someone explain why these two codes do not function the same

Because you do different things. std::vector::erase function takes iterators as arguments - special objects which refer to specific position within a vector. You can dereference an iterator because it has dereference operator overloaded, however (&num) doesn't turn the num into the iterator, it's still a pointer, which is just not compatible with std::vector::erase function parameters.

if there is a way of accessing the range-for-loop's internal iterator

Not really. You are trying to alter a container which you are currently going through, that is error-prone on its own. For iterator it works only because you invalidate and update your iterator on each call to erase:

nums.erase(itr--);

itr-- makes the iterator to point to the previous value and returns the iterator which points to current one (so you can remove the result of this operation freely, because it's now a temporary value, and your local iterator variable itr no longer points to this position)

CodePudding user response:

The two loops you have provided are not equivalent. The first loops across iterators to elements in the vector. The second loops across the values stored in the vector. For a vector of integers, the type for num is integer. The erase method for vectors requires either an iterator or a pair of iterators to mark the elements to remove. So even &num will not work as it is not providing an iterator.

For reference:

https://en.cppreference.com/w/cpp/container/vector/erase

https://en.cppreference.com/w/cpp/language/range-for

Consider using std::unique:

https://en.cppreference.com/w/cpp/algorithm/unique

CodePudding user response:

It would be more correctly to rewrite the first code snippet at least like

for ( auto itr = nums.begin(); itr != nums.end(); ) {
    if(*itr != set) {
        set = *itr  ;
    } else {
        itr = nums.erase( itr );
    }
}

After erasing an element iterators starting from the iterator of the erased element become invalid.

So this range-based for loop

for(auto num : nums) {
    if(num != set) {
        set = num;
    } else {
        nums.erase((&num));
    }
}

invokes undefined behavior because it is based on iterators. Moreover in modern C standard libraries iterators of the class template std::vector are not pointers. So moreover this statement

nums.erase((&num));

is just invalid. You shall not rely on that iterators of the class template std::vector are defined as pointers.

Pay attention to that if you want to remove adjacent equal elements then you could use the standard algorithm std::unique as for example

nums.erase( std::unique( nums.begin(), nums.end() ), nums.end() );

Here is a demonstration program.

#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>

int main()
{
    std::vector<int> nums = { 1, 2, 2, 3, 3, 3, 4, 4, 4, 4 };

    for ( const auto &item :nums )
    {
        std::cout << item << ' ';
    }
    std::cout << '\n';

    nums.erase( std::unique( std::begin( nums ), std::end( nums ) ), 
                std::end( nums ) );

    for ( const auto &item :nums )
    {
        std::cout << item << ' ';
    }
    std::cout << '\n';
}

The program output is

1 2 2 3 3 3 4 4 4 4 
1 2 3 4 
  • Related