0

I have a chunk of C++ code that is supposed to go through a sorted 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; //sets the current loop number equal to a new number
    } else {
        nums.erase(itr--); //erases the number if it is repeating
    }
}

C2)

for(auto num : nums) {
    if(num != set) {
        set = num;
    } else {
        nums.erase((&num));
    }
}
6
  • 1
    Does this answer your question? Can you remove elements from a std::list while iterating through it? Commented Aug 2, 2022 at 18:34
  • what are the compiler errors? In the second code num is a copy of the elements in the list, and &num isn't what you want to erase Commented Aug 2, 2022 at 18:40
  • 2
    No, there's no way to access ranged-for's internal iterator, so it's not possible to use it here. Commented Aug 2, 2022 at 18:40
  • auto num creates copies. auto &num does not. Commented Aug 2, 2022 at 18:51
  • @HolyBlackCat Yes, pity, that. Commented Aug 2, 2022 at 19:09

3 Answers 3

2

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)

Sign up to request clarification or add additional context in comments.

Comments

0

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

Comments

0

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 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.