Documentation Index
Fetch the complete documentation index at: https://resources.devweekends.com/llms.txt
Use this file to discover all available pages before exploring further.
The Standard Template Library (STL)
The STL is C++‘s superpower. It provides efficient, tested, and reusable components. Rule #1 of C++: If it’s in the STL, don’t write it yourself. Why write a buggy linked list whenstd::list exists and is optimized by experts?
1. Containers
Containers are data structures that store collections of objects. They manage memory automatically (using RAII internally), so you never need tonew/delete when using them. Picking the right container is one of the most impactful performance decisions you will make in C++.
Sequential Containers (Ordered)
std::vector (Dynamic Array)
- What is it?: A contiguous, dynamically-growing array. Elements are stored side-by-side in memory, which means the CPU cache can prefetch them efficiently.
- Use by default: 99% of the time,
vectoris the right choice. Even for small collections where you might think a linked list is “theoretically better,”vectorwins because of cache locality. Bjarne Stroustrup himself has demonstrated this in benchmarks. - Operations: O(1) random access (
[]), O(1) amortizedpush_back, O(n) insert in the middle.
std::array (Fixed-Size Array)
- What is it?: A safer version of C-style arrays (
int arr[5]). - Use when: Size is known at compile time and won’t change.
- Zero overhead: No dynamic allocation.
Associative Containers (Key-Value / Sorted)
std::map (Ordered Map)
- What is it?: A tree-based dictionary.
- Order: Keys are always sorted (alphabetically or numerically).
- Complexity: O(log n).
Unordered Containers (Hash Tables)
std::unordered_map
- What is it?: A hash table. Keys are hashed into buckets for near-instant lookup.
- Order: No guaranteed order. Elements may appear in any sequence when iterating.
- Complexity: O(1) average for insert, find, and erase. O(n) worst case if many keys hash to the same bucket (hash collision).
- Use when: You need fast lookups and do not care about iteration order. This is the right choice for caches, memoization tables, and symbol tables.
Container Performance Comparison
| Container | Access | Insert (end) | Insert (middle) | Find | Erase | Memory layout | Sorted? |
|---|---|---|---|---|---|---|---|
vector | O(1) index | O(1) amortized | O(n) | O(n) linear / O(log n) sorted | O(n) | Contiguous | No (unless you sort it) |
deque | O(1) index | O(1) front and back | O(n) | O(n) | O(n) | Chunked blocks | No |
list | O(n) | O(1) anywhere (with iterator) | O(1) with iterator | O(n) | O(1) with iterator | Scattered nodes | No |
array | O(1) index | N/A (fixed size) | N/A | O(n) | N/A | Contiguous, stack-allocated | No |
map | O(log n) | O(log n) | N/A | O(log n) | O(log n) | Red-black tree nodes | Yes (by key) |
unordered_map | O(1) avg | O(1) avg | N/A | O(1) avg | O(1) avg | Hash table + buckets | No |
set | O(log n) | O(log n) | N/A | O(log n) | O(log n) | Red-black tree nodes | Yes |
unordered_set | O(1) avg | O(1) avg | N/A | O(1) avg | O(1) avg | Hash table | No |
Container Selection Quick Guide
Choosing the right container matters. Here is a practical decision tree:- Need a sequence of elements? Use
std::vector(default) orstd::deque(fast insert at front). - Need key-value pairs with sorted keys? Use
std::map. - Need key-value pairs with fast lookup? Use
std::unordered_map. - Need to ensure no duplicates? Use
std::setorstd::unordered_set. - Need a fixed-size array? Use
std::array. - Need a FIFO queue? Use
std::queue(adapter overstd::deque). - Need a priority queue? Use
std::priority_queue.
The
vector almost always wins in practice: Even for operations where list has better theoretical complexity (O(1) insert vs O(n)), std::vector is often faster for collections under ~10,000 elements because of CPU cache effects. Contiguous memory means the CPU prefetcher loads the next elements before you ask for them. Linked list nodes are scattered across the heap, causing cache misses on every traversal. Bjarne Stroustrup’s benchmarks show vector beating list for insert-in-the-middle operations up to surprisingly large sizes. Default to vector and switch only when profiling proves otherwise.When to Reach for Something Other Than vector
| Situation | Use instead | Why |
|---|---|---|
| Frequent insert/remove at the front | std::deque | O(1) at both ends, no reallocation of existing elements |
| Elements must not move in memory (stable addresses) | std::list or std::deque | vector reallocation invalidates all pointers/references |
| Need sorted iteration AND fast lookup | std::map or std::set | Tree structure keeps elements ordered at all times |
| Need O(1) key lookup, order does not matter | std::unordered_map | Hash table is 3-10x faster than map for large datasets |
| Fixed number of elements known at compile time | std::array | Zero heap allocation, same performance as C array |
| Need a thread-safe FIFO queue | Build on std::queue + mutex (or use a lock-free queue library) | No STL container is thread-safe by default |
2. Iterators
Iterators are the glue between Containers and Algorithms. Think of them as a universal “cursor” that knows how to move through any container, regardless of its internal structure. A vector iterator moves through contiguous memory; a map iterator walks a balanced tree. But the interface is the same — that is the power of abstraction.begin(): Points to the first element.end(): Points to one past the last element (this is a sentinel, not a valid element).
Range-Based For Loop
This syntactic sugar uses iterators behind the scenes but is much cleaner. Prefer this in all new code.3. Algorithms
The<algorithm> header contains 100+ functions for sorting, searching, and manipulating ranges. They are often faster and less buggy than writing your own loops.
Sorting & Searching
Transformations
Finding
4. Lambdas (Anonymous Functions)
Lambdas allow you to write small, throwaway functions inline. They are perfect for passing to algorithms likesort or find_if. Before C++11, you had to write separate functor classes for this — lambdas eliminated that boilerplate entirely.
Syntax: [capture](parameters) -> return_type { body }
Capture Clauses
[]: No capture. The lambda cannot access any variables from the enclosing scope. This is the safest option.[=]: Capture all local variables by value (copy). The lambda gets its own snapshot.[&]: Capture all local variables by reference (can modify originals).[x]: Capture onlyxby value. Prefer explicit captures — they make dependencies clear.[&x]: Capture onlyxby reference.[=, &x]: Capture everything by value, exceptxby reference.
Summary
- Containers: Use
std::vectorby default. Usestd::mapfor sorted keys,std::unordered_mapfor speed. - Iterators: Abstract the navigation of data.
- Algorithms: Prefer
std::sort,std::find, etc., over writing raw loops. - Lambdas: Write concise, local functions for algorithms.