STL can first look like a bag of unrelated names: vector, map, begin() and sort(). Memorising each name separately makes even short programs confusing because it hides how the pieces cooperate. One mental model carries all of it: a container owns data, iterators identify a range, and an algorithm performs an operation on that range. Seven exam scores, 72, 88, 72, 95, 64, 88 and 81, exercise the whole model: one vector holds them, one sort orders them, one lower_bound finds the first score of at least 80, one predicate drops everything under 70, and one accumulate totals what remains. STL is not a separate language, and not every standard-library facility belongs to it: std::string and the iostreams ship with the standard library but sit outside the container, iterator and algorithm core.
1. What STL contains and how its parts fit together
Containers store values, iterators move through them, and algorithms such as sort, find, count and remove_if accept iterator ranges. Function objects and lambdas supply policies, such as [](int score) { return score < 70; } for selecting low scores.
Most STL ranges are half-open: [first, last). first is included, while last points just after the final element and must never be dereferenced. In vector<int> v{10, 20, 30, 40}; sort(v.begin() + 1, v.end());, the range covers 20, 30, 40; 10 remains outside it.
Part | Job | Running example |
|---|---|---|
| Own values | Store seven scores |
| Delimit a range | Cover all seven scores |
| Reorder a range | Put scores in ascending order |
Lambda | State a policy | Select scores below 70 |
That three-part split is not unique to C++. Java collections and Python's standard library also separate storage, traversal and operation in the same way, which is why the model is worth learning once and reusing. The Coding and DSA courses cover the C, C++, Java and Python versions of it.
2. Choosing an STL container before writing the algorithm
Choose by the problem, not familiarity. Sequence containers are vector, deque, list, forward_list and array. Ordered associative containers include set, multiset, map and multimap; unordered choices include unordered_set and unordered_map. Adapters such as stack, queue and priority_queue deliberately expose a restricted interface instead of general iterator ranges.
Need | Choice | Useful property |
|---|---|---|
Indexed access and appending |
| Constant-time indexing; amortised constant-time |
Key-value data in key order |
| Logarithmic lookup and insertion |
Key-value data without order |
| Average constant-time operations, possible linear worst cases |
Unique ordered values |
| Uniqueness plus ordering |
Retrieve the current largest first |
| Direct access to the maximum |
For {72, 88, 72, 95, 64, 88, 81}, a vector preserves the full input. A map<int, int> can count each score, a set<int> keeps unique sorted values, and a priority_queue<int> retrieves the maximum first. Using a set for the original input would silently discard the second 72 and second 88.
3. Runnable STL example: sort, search, filter and aggregate seven scores
This program compiles as C++17. <algorithm> supplies sort, lower_bound, remove_if and count; <numeric> supplies accumulate; and <iomanip> supplies setprecision for the two-decimal average.
#include <algorithm>
#include <iomanip>
#include <iostream>
#include <numeric>
#include <vector>
int main() {
std::vector<int> scores{72, 88, 72, 95, 64, 88, 81};
std::sort(scores.begin(), scores.end());
std::cout << "sorted:";
for (int score : scores) std::cout << ' ' << score;
std::cout << '\n';
auto first80 = std::lower_bound(scores.begin(), scores.end(), 80);
auto first80Index = first80 - scores.begin();
std::cout << "first score >= 80: " << *first80
<< " at index " << first80Index << '\n';
auto newEnd = std::remove_if(scores.begin(), scores.end(),
[](int score) { return score < 70; });
scores.erase(newEnd, scores.end());
std::cout << "after removing scores < 70:";
for (int score : scores) std::cout << ' ' << score;
std::cout << '\n';
int count88 = std::count(scores.begin(), scores.end(), 88);
int total = std::accumulate(scores.begin(), scores.end(), 0);
double average = static_cast<double>(total) / scores.size();
std::cout << "count of 88: " << count88 << '\n';
std::cout << "total: " << total << ", average: "
<< std::fixed << std::setprecision(2) << average << '\n';
}Sorting produces [64, 72, 72, 81, 88, 88, 95]. Since that range is ordered, lower_bound(..., 80) points to 81 at zero-based index 3. The predicate removes only 64, leaving [72, 72, 81, 88, 88, 95]. Counting 88 gives 2. The sum is 72 + 72 + 81 + 88 + 88 + 95 = 496; dividing by 6 gives 82.666..., printed to two decimal places as 82.67.
sorted: 64 72 72 81 88 88 95
first score >= 80: 81 at index 3
after removing scores < 70: 72 72 81 88 88 95
count of 88: 2
total: 496, average: 82.67std::sort is required to do O(n log n) comparisons, and typical implementations use an introsort hybrid rather than one textbook algorithm. The comparison of sorting algorithms and their complexity works through where those bounds come from and which algorithms stay stable.

4. Iterators, erase-remove and invalidation rules
Iterator category controls which algorithms are legal. A vector has random-access iterators, so std::sort works. A list has bidirectional iterators, making std::sort(list.begin(), list.end()) ill-formed; use list.sort(). A priority_queue exposes no iterators, only adapter operations such as top, push and pop.
remove_if moves the retained elements to the front and returns a new logical end. It does not reduce scores.size(): the elements from newEnd to the old end() are still valid objects, but their values are unspecified because they have been moved from. erase(newEnd, scores.end()) performs the physical size change from 7 to 6.
A vector reallocation invalidates all iterators, pointers and references. Erasure invalidates iterators at and after the erased position. In map and set, insertions and erasures leave iterators to other elements valid, but the erased element's iterator becomes invalid. The program therefore calculates first80Index before erase. It must not dereference first80 afterwards without reacquiring it.
5. Extend the same data with map, set and priority_queue
Rebuild the original seven-score vector, and three containers answer three different questions about it: how often each score occurs, which scores are distinct, and which score is highest.
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <vector>
int main() {
std::vector<int> scores{72, 88, 72, 95, 64, 88, 81};
std::map<int, int> frequency;
for (int score : scores) ++frequency[score];
for (const auto& entry : frequency)
std::cout << entry.first << " -> " << entry.second << '\n';
std::set<int> distinct(scores.begin(), scores.end());
std::cout << "distinct values: " << distinct.size() << '\n';
std::priority_queue<int> pending(scores.begin(), scores.end());
std::cout << "top three:";
for (int i = 0; i < 3; ++i) {
std::cout << ' ' << pending.top();
pending.pop();
}
std::cout << '\n';
}64 -> 1
72 -> 2
81 -> 1
88 -> 2
95 -> 1
distinct values: 5
top three: 95 88 88Ordered map iteration walks the keys ascending, so it prints 64 -> 1, 72 -> 2, 81 -> 1, 88 -> 2, 95 -> 1. An unordered_map can count too, but its iteration order is not promised.
std::set<int> distinct(scores.begin(), scores.end()); produces [64, 72, 81, 88, 95]. Five elements are correct only when uniqueness is intended, because the duplicate 72 and the duplicate 88 are gone. Both map and set are ordered search structures, so lookup, insertion and erasure are logarithmic rather than constant, although the standard fixes the complexity bounds and leaves the tree implementation open.
With std::priority_queue<int> pending(scores.begin(), scores.end());, three top() and pop() calls yield 95, 88, 88. Duplicates remain. The adapter exposes the highest-priority element, not a sorted iterable view.

6. STL mistakes that make correct-looking code fail
Dereferencing scores.end() is undefined behaviour. Calling lower_bound on the original unsorted sequence has no meaningful sorted-search answer because its ordering precondition is missing. A wrong iterator pair can exclude required values or form an invalid range.
Map lookup can mutate data. Given std::map<std::string, int> attempts{{"Asha", 2}};, attempts["Kabir"] returns 0, inserts "Kabir", and increases the size to 2. Use find() for a read-only C++17 check. contains() requires C++20.
Three quick repairs are worth remembering:
If
unordered_mapoutput appears in an unexpected order, sort the keys or usemap.If
remove_ifleaves the physical size unchanged, complete the erase-remove idiom.If a header uses
using namespace std;, remove it and qualify names instead, because every file that includes that header inherits the same name collisions.
7. How coding tests and interviews assess STL choices
Test and interview questions on STL usually turn on a single decision: which container the requirement forces. Indexed scores that keep duplicates call for vector. Unique sorted scores call for set. Frequency by sorted score calls for map, while frequency with fast average lookup and no ordering requirement calls for unordered_map. Repeated retrieval of the current highest score calls for priority_queue. A complete answer says what happens to duplicates, whether ordering is promised, what the complexity is, and which iterators survive the mutation.
Predict before running: lower_bound(80) returns 81 at index 3 after sorting. Erase-remove leaves size 6 and retains both 88s. A set built from the seven original values has size 5.
For practice, create vector<vector<int>> adjacency(4) with undirected edges 0-1, 0-2 and 1-3. A queue-based BFS from 0, visiting neighbours in stored order, yields 0, 1, 2, 3, and std::queue is the adapter that keeps that loop to a few lines. Graph algorithms: BFS, DFS and Dijkstra traced step by step carries the same traversal further, and the C++ track of Coding For Placements has the practice problems.
8. STL in C++: the short version and next step
Remember four rules:
Choose a container by ownership, ordering and duplicate requirements.
Use iterators to describe a valid half-open range.
Check an algorithm's iterator and ordering preconditions.
Re-check iterator validity after every mutation.
The correctness check is compact: seven inputs sort to [64, 72, 72, 81, 88, 88, 95]; filtering removes 64, and the remaining six total 496 with average 82.67.
The C++ track of the C++ Programming course covers the language from the first program upward, which is the ground the STL sits on. Before moving on, rerun the program with a cutoff of 85 instead of 70. Predict [88, 88, 95], total 271 and average 90.33, then compile it and check.




