6

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 ?

2
  • 4
    No. Removal is an action on the vector itself. The pattern of the standard algorithm std::remove_if is to move the elements to the end, then you can erase them afterwards. Commented Mar 2, 2016 at 11:56
  • Can't be bothered to write up a full answer, but a one-liner to do this is: vec.erase(std::remove_if(std::begin(vec), std::end(vec), [](int i){return !(i%2);}), std::end(vec)); Commented Mar 2, 2016 at 12:09

5 Answers 5

5

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 
Sign up to request clarification or add additional context in comments.

Comments

1

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

4 Comments

Can you explain why the approach he wanted is not going to work, and what is good about your approach?
I am trying to imitate the way that the standard library works. I have imitated the behavior of remove_if
@V-R that's an abbreviated function template, which will likely be a C++17 feature. Humam: I'd suggest making it a standard template as using this feature detracts from the point of your answer.
Done. Thanks for notifying
1

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.

Comments

0

There is no way to do this by passing just the iterators to a function. You have to pass the whole vector to the function as shown below:

void remove_even(std::vector<int> v) { for (auto it = v.begin(); it != v.end();) { if ((*it) % 2 == 0) { it = v.erase(it); } else { ++it; } } } 

Comments

0

You may want to consider the idiom of passing the container by x-value reference and then returning a copy/moved version.

This gives the advantages of efficiency coupled with readability at the call site.

An extended example below.

Note that all the 'apparent' copying of the vector will be elided (optimised away) by the compiler (RVO).

Also note that because Container&& is evaluated in deduced context, it will automatically be either a const Container& or a Container&&, whichever is most appropriate in the context of the call site. This means the same function can be used to both mutate an existing container (x = sorted(x)) or to make a mutated copy (x = sorted(y))

#include <iostream> #include <vector> #include <algorithm> template<class Container, class Predicate> auto all_except(Container&& c, Predicate&& pred) { auto result = std::forward<Container>(c); result.erase(std::remove_if(std::begin(result), std::end(result), std::forward<Predicate>(pred)), std::end(result)); return result; } template<class Container> auto sorted(Container&& c) { auto result = std::forward<Container>(c); std::sort(std::begin(result), std::end(result)); return result; } auto even_items = [](auto const& item) { return item % 2 == 0; }; auto odd_items = [](auto const& item) { return item % 2 != 0; }; template<class Vec> void emit(const Vec& v) { std::cout << "["; auto sep = " "; for (auto const& e : v) { std::cout << sep << e; sep = ", "; } std::cout << " ]" << std::endl; } int main() { std::vector<int> vec = {65, 2, 32, 63, 9, 13, 88, 22, 4, 5, 6, 7}; emit(all_except(vec, even_items)); emit(all_except(vec, odd_items)); emit(all_except(sorted(vec), odd_items)); vec = sorted(all_except(std::move(vec), even_items)); emit(vec); return 0; } 

expected results:

[ 65, 63, 9, 13, 5, 7 ] [ 2, 32, 88, 22, 4, 6 ] [ 2, 4, 6, 22, 32, 88 ] [ 5, 7, 9, 13, 63, 65 ] 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.