Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deleting an element of a container by directly accessing its iterator alone in C++

I have declared a std::vector<int> in my main function and would like to remove all even elements from it, but only by passing it's iterator to a function called remove_even which accepts the start and end iterator of the container.

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

void remove_even(auto start, auto end) {
    while(start != end) {
        if(*start % 2 == 0)
        // Remove element from container
    }
}

int main() {
    std::vector<int> vec = {2, 4, 5, 6, 7};
    remove_even(vec.begin(), vec.end());
}

Is there a way of doing this in C++ or must I directly pass in my vector to the function ?

like image 964
Mutating Algorithm Avatar asked Mar 02 '16 11:03

Mutating Algorithm


3 Answers

It is the class std::vector itself that has the method erase that allows to erase required elements in a vector.

All you can do using iterators is to call standard algorithm std::remove_if that then to use the returned iterator in a call of the method erase.

For example

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

std::vector<int>::iterator remove_even( std::vector<int>::iterator first,
                                        std::vector<int>::iterator last )
{
    return std::remove_if( first, last, []( int x ) { return x % 2 == 0; } );
}

int main()
{
    std::vector<int> vec = { 2, 4, 5, 6, 7 };

    for ( int x : vec ) std::cout << x << ' ';
    std::cout << std::endl;

    vec.erase( remove_even( vec.begin(), vec.end() ), vec.end() );

    for ( int x : vec ) std::cout << x << ' ';
    std::cout << std::endl;
}    

The program output is

2 4 5 6 7 
5 7 
like image 89
Vlad from Moscow Avatar answered Nov 18 '22 20:11

Vlad from Moscow


This is the correct way IMO:

template <typename T>
T remove_even(T start, T end) {
   return std::remove_if(start,end,[](const auto& item){return item%2==0;};
}

int main() {
    std::vector<int> vec = {2, 4, 5, 6, 7};
    vec.erase(remove_even(vec.begin(), vec.end()),vec.end());
}
like image 31
Humam Helfawi Avatar answered Nov 18 '22 22:11

Humam Helfawi


From cplusplus.com:

An iterator is any object that, pointing to some element in a range of elements (such as an array or a container), has the ability to iterate through the elements of that range using a set of operators (with at least the increment (++) and dereference (*) operators).

As stated in the quote, it points to an element in a range of elements. It does not need to provide any information about the range it is in, i.e. about the container the elements are stored in.

As already stated in a comment, removing an element from a vector (or any other container) is an operation that affects the container, not only the object. So you will always have to call erase() or a similar function on the container.

What you are asking about is (somewhat, not exactly) similar to this:

void remove_or_not(int& i){
    //do something with i to remove it from a container
    //but we dont have a container here
}

int main(){
    std::vector<int> vec;
    //fill vec and generate some int n
    remove_or_not(vec[n]);
}

When calling remove_or_not() in the above example, we just pass a reference to an int - we totally lost the information that it is inside a container, so it's obvious we can't remove it from any container.

When trying the same with iterators we still have the information that the element is inside a container - but we might have lost the information in which container, as it is not required by the iterator to keep this information.
E.g. an iterator over a C-style array can just be a pointer. We can increment and decrement it and compare it to a pointer to the first element and a pointer behind the last element. But there is no need to know anything about the size of the array or about the array at all.

PS: for approaches how to correclty implement what you need, see the already posted answers, i don't think theres a need to repeat those.

like image 1
Anedar Avatar answered Nov 18 '22 22:11

Anedar