Skip to content

41 Intermediate The Hundred

std::remove Removes Nothing

the removal that never happens

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

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

    std::remove(v.begin(), v.end(), 2);   // delete every 2... right?

    std::cout << "size: " << v.size() << '\n';
    for (int x : v)
        std::cout << x << ' ';
    std::cout << '\n';
}

Run it. What does it print?

Answer

size: 5, then 1 3 4 2 4 (typical output, GCC on x86-64). All five elements are still there — including a 2.

Why

std::remove sees the world through a pair of iterators — it has no idea a vector sits behind them, so it couldn't resize the container even if it wanted to. All an algorithm can do is shuffle values: it shifts every kept element toward the front and returns an iterator to the new logical end. Everything between that iterator and v.end() is leftover — valid but unspecified values per the standard; in practice the old tail, which is where the stray 2 4 comes from. The size never changes, and by discarding the return value the snippet throws away the one thing remove actually produced. Don't expect help: g++ 11 compiles this warning-free even with -Wall -Wextra. std::unique sets the exact same trap.

The fix

Feed the returned iterator straight into erase — the classic erase–remove idiom:

v.erase(std::remove(v.begin(), v.end(), 2), v.end());   // size: 3 → 1 3 4

Since C++20 there's a one-liner that does both jobs: std::erase(v, 2);.

Takeaway: std::remove only computes what to keep — it takes erase (a container member) to actually shrink; never call one without the other.

Try it: g++ -std=c++17 main.cpp -o demo && ./demo

Open in Compiler Explorer ↗ Quiz this entry