Loops You Don't Have to Write
On the previous page you saw that every container hands out iterators - lightweight cursors with begin() and end(). That abstraction is the whole reason the standard algorithms exist. Instead of writing a raw for loop every time you want to search, count, or transform data, you call a named function from <algorithm> and hand it a range.
A range is just two iterators: where to start and one-past-where to stop. Because every algorithm speaks this same iterator language, the same find works on a vector, a string, or a plain array.
The pattern to internalize: an algorithm that searches returns the end() iterator to mean "nothing found". Always compare against end() before dereferencing the result - dereferencing end() is undefined behavior.
Counting and Testing with Predicates
Many algorithms take a predicate - a function (usually a lambda) that returns bool for each element. count_if counts matches; all_of, any_of, and none_of answer yes/no questions about the whole range.
std::count (no _if) is the simpler cousin that counts an exact value instead of a condition. Reach for the predicate versions the moment your test is "anything matching a rule" rather than "this specific value".
Transforming and Reducing a Range
Two workhorses cover most data processing: std::transform maps each element through a function, and std::accumulate (from <numeric>, not <algorithm>) folds a range down to a single value.
transform writes its results through an output iterator. A common and dangerous mistake is pointing the output at an empty vector - the algorithm assumes room already exists and writes past the end. Either size the destination first, or use a back_inserter so each result is push_backed.
accumulate starts from an initial value and combines elements left to right. The initial value's type matters: pass 0 (an int) and the sum is computed in int, which can overflow or truncate depending on the element data type.
Had you written accumulate(prices.begin(), prices.end(), 0) with an int seed, each addition would happen in int and the cents would vanish. The seed type silently drives the result type.
The Erase-Remove Idiom
Here is the gotcha that surprises everyone. std::remove does not remove anything from the container. Algorithms only have iterators, so they cannot change a container's size - they have no idea a container is even there. What remove actually does is move all the kept elements to the front, leaving the tail in an unspecified state, and return an iterator to the new logical end.
// remove alone leaves the size unchanged - this is the bug:
remove(v.begin(), v.end(), 0); // returns an iterator you ignored
// v still has its original size; the tail is garbage
To truly delete the elements you pair remove with the container's erase, which is why it's called the erase-remove idiom:
Use remove_if for a predicate instead of an exact value. In C++20 you can skip the dance entirely with the free function std::erase / std::erase_if, which do both steps for you: erase(v, 0);.
Iterators Outlive Their Validity at Your Peril
Because algorithms return iterators, those iterators are subject to the same invalidation rules you met on the previous page. Storing an iterator and then modifying the container - push_back that triggers a reallocation, or an erase - can leave the stored iterator dangling, and using it is undefined behavior.
auto it = find(v.begin(), v.end(), 16);
v.push_back(99); // may reallocate v's storage
cout << *it; // BUG: `it` may now point at freed memory
The safe habit: use the iterator an algorithm returns immediately, before any operation that could resize or reallocate the container. If you need to mutate the container based on a result, capture an index (it - v.begin()) instead, since indices survive reallocation.
Next: Sorting
You've now seen searching, counting, transforming, and reducing - but one algorithm is important enough to deserve its own page. std::sort reorders a range in place, and once data is sorted a whole family of faster, sorted-only algorithms (binary_search, lower_bound, equal_range) opens up. Next we'll dig into sorting: how to supply a custom comparator, the difference between sort and stable_sort, and the rules your comparison function must obey to avoid undefined behavior.
Frequently Asked Questions
What is the <algorithm> header in C++?
<algorithm> is the standard-library header holding generic functions like std::find, std::sort, std::count_if, and std::transform. They operate on ranges described by a pair of iterators (usually begin() and end()), so the same algorithm works on a vector, array, string, or any container that exposes iterators.
How do I check if a value exists in a vector in C++?
Use std::find: auto it = find(v.begin(), v.end(), target);. If it == v.end() the value isn't present; otherwise it points at the first match. To test a condition instead of an exact value, use std::any_of with a predicate.
Why doesn't std::remove actually remove elements from my container?
Algorithms only see iterators, not the container, so std::remove can't shrink it - it shuffles the kept elements to the front and returns an iterator to the new logical end. You must follow it with v.erase(...) (the erase-remove idiom) to physically drop the leftovers.