1

Possible Duplicate:
STL remove doesn’t work as expected?

Sorry, I'm new to C++11 and iterators. This is supposed to remove all the number 3's in the array, but it doesn't remove the last one. Why?

#include <algorithm>
#include <array>
#include <iostream>

int main() {

   std::array<int, 8> a{{9, 3, 4, 5, 33, 5, 6, 3}};

   int N(3);

   std::remove(a.begin(), a.end(), N);

   for (int i : a) {
      std::cout << i << '\n';
   }

}

I get as output:

{ 9, 4, 5, 33, 5, 6, 6, 3 }
                        ^
                        |
             // the last 3 is still there
Community
  • 1
  • 1
Me myself and I
  • 3,990
  • 1
  • 23
  • 47

3 Answers3

6

std::remove operates on iterators; as such, it has no way of actually erasing the elements from the container. That's why its generally used together with erase:

a.erase(std::remove(a.begin(), a.end(), N), a.end());

As others have noted, this won't work for array.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
K-ballo
  • 80,396
  • 20
  • 159
  • 169
  • What is `std::remove` doing in this context? I know it's returning an iterator, but how does that (in addition to the arguments given to it) affect how `erase` handles it? – Me myself and I Dec 30 '12 at 20:57
  • @David: _chris_ explained quite well in his answer. If you read the reference, what `remove` does is move all the kept elements to the front effectively _removing_ the elements you want to remove, and leaving what for all practical purposes is garbage at the back of your sequence. What `erase` does is clean that garbage left by `remove`. – K-ballo Dec 30 '12 at 20:58
6

Algorithms have no knowledge of the underlying container. They just iterate through based on what iterators they are given and access elements independently of the container holding them. That's why there is something called the erase-remove idiom, which takes the following pattern:

container.erase(std::remove(it1, it2, value), std::end(container));

std::remove moves the kept elements to the front (thanks, K-ballo) and returns an iterator to the start of the unmoved elements. Then, erase erases everything from that point to the end.

Since std::array encapsulates a fixed-size array, I'll adapt your example for std::vector:

std::vector<int> v{9, 3, 4, 5, 33, 5, 6, 3};
v.erase(std::remove(std::begin(v), std::end(v), 3), std::end(v));

The only other thing to note is the more general form of .begin() and .end(), which works on built-in arrays as well. This is included in C++11.

chris
  • 60,560
  • 13
  • 143
  • 205
  • 2
    `remove` doesn't move the removed elements to the back, it moves the keept elements to the front. The elements at the back have an unspecified value. – K-ballo Dec 30 '12 at 20:52
  • @K-ballo, Oh, I didn't realize that. I was going off of memory. I'll fix it up a bit later. – chris Dec 30 '12 at 20:53
2

As was pointed out by other, std::remove() won't remove any elements. If you need to use std::array<...> you can still use std::remove() and just capture the corresponding iterator to determine the end of the sequence. How you got the claimed output (with the curlies and the commas) I don't know but here is how I'd implement it:

auto end = std::remove(a.begin(), a.end(), 3);
std::cout << "{ ";
if (a.begin() != end) {
    std::copy(a.begin(), end - 1, std::ostream_iterator<int>(std::cout, ", "));
    std::cout << end[-1];
}
std::cout << "}\n";
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380