C++
std::vector
erase elements
vector indexing
programming tutorial

Erasing elements in stdvector by using indexes

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

Removing items from a std::vector by index is easy for one element and surprisingly easy to get wrong for many elements. The reason is that erase shifts later elements left, so every removal changes the indexes that follow.

Removing One Element by Index

For a single index, use erase with an iterator:

cpp
1#include <iostream>
2#include <vector>
3
4int main() {
5    std::vector<int> values{10, 20, 30, 40, 50};
6    std::size_t index = 2;
7
8    if (index < values.size()) {
9        values.erase(values.begin() + static_cast<std::ptrdiff_t>(index));
10    }
11
12    for (int value : values) {
13        std::cout << value << ' ';
14    }
15}

Output:

text
10 20 40 50

The bounds check matters. values.begin() + index is undefined behavior if index is past the end.

Removing Multiple Indexes Safely

Suppose you want to remove indexes 1, 3, and 4 from the original vector. If you erase them in ascending order, the later positions no longer refer to the same elements after the first erase.

The safest direct approach is to erase in descending order:

cpp
1#include <algorithm>
2#include <iostream>
3#include <vector>
4
5int main() {
6    std::vector<int> values{10, 20, 30, 40, 50, 60};
7    std::vector<std::size_t> indexes{1, 3, 4};
8
9    std::sort(indexes.rbegin(), indexes.rend());
10
11    for (std::size_t index : indexes) {
12        if (index < values.size()) {
13            values.erase(values.begin() + static_cast<std::ptrdiff_t>(index));
14        }
15    }
16
17    for (int value : values) {
18        std::cout << value << ' ';
19    }
20}

Because the largest index is removed first, earlier indexes stay valid relative to the original layout.

When Many Indexes Need Removal

Repeated erase calls can be expensive because every erase shifts part of the vector. If you are removing many elements, it is often faster to build a keep-mask and compact in one pass.

cpp
1#include <iostream>
2#include <unordered_set>
3#include <vector>
4
5int main() {
6    std::vector<int> values{10, 20, 30, 40, 50, 60};
7    std::unordered_set<std::size_t> to_remove{1, 3, 4};
8
9    std::vector<int> kept;
10    kept.reserve(values.size());
11
12    for (std::size_t i = 0; i < values.size(); ++i) {
13        if (!to_remove.contains(i)) {
14            kept.push_back(values[i]);
15        }
16    }
17
18    values = std::move(kept);
19
20    for (int value : values) {
21        std::cout << value << ' ';
22    }
23}

This approach does not preserve original iterators, but neither does erase. It is often easier to reason about when the removal set is large.

Range Erasure for Contiguous Indexes

If the indexes form one continuous block, erase the whole range at once:

cpp
std::vector<int> values{10, 20, 30, 40, 50};
values.erase(values.begin() + 1, values.begin() + 4);

That removes the elements originally at indexes 1, 2, and 3, leaving 10 50.

Range erase is clearer and usually faster than three separate single-element erasures.

Iterator and Reference Invalidation

Any erase from a vector invalidates iterators and references at or after the erased position. That means this pattern is dangerous:

cpp
auto it = values.begin() + 3;
values.erase(values.begin() + 1);
std::cout << *it << '\n';

After the erase, it may no longer be valid. If you need to keep working with positions after removals, recompute them from the current vector state.

Common Pitfalls

The biggest mistake is erasing multiple indexes in ascending order. After the first erase, the remaining indexes no longer point to the same elements.

Another problem is skipping bounds checks. Vectors do not protect you from invalid iterator arithmetic.

Developers also forget about iterator invalidation and keep references to elements that have been shifted or destroyed.

Finally, do not assume repeated erase is the best option for large removal sets. Rebuilding the vector can be simpler and more efficient.

Summary

  • Use erase(begin() + index) for a single valid index.
  • For multiple removals, erase indexes in descending order.
  • Use range erase when the indexes are contiguous.
  • Expect iterators and references after the erased position to become invalid.
  • For many removals, rebuilding the vector in one pass is often cleaner and faster.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.