Worked examples — STL algorithms — sort, find, transform, accumulate, copy, all_of, any_of
The scenario matrix
Before solving anything, let's map out every case class these 7 algorithms can hit. Each row is a "corner" of behaviour; the last column names the worked example that lands on it.
| # | Algorithm | Case class (the tricky corner) | Covered by |
|---|---|---|---|
| A | sort |
custom comparator, stability & ties | Ex 1 |
| B | sort |
degenerate: empty / single-element range | Ex 1 (part b) |
| C | find |
value present vs absent (the last sentinel) |
Ex 2 |
| D | transform |
in-place alias (dest == first) |
Ex 3 |
| E | transform |
two-range binary op | Ex 3 (part b) |
| F | accumulate |
type trap: int init over double data |
Ex 4 |
| G | accumulate |
non-+ fold; identity element matters |
Ex 5 |
| H | copy |
no room → back_inserter fix |
Ex 6 |
| I | all_of / any_of |
empty range vacuous truth / falsity | Ex 7 |
| J | all_of / any_of |
short-circuit stops early | Ex 7 (part b) |
| K | Real-world word problem | combine several algorithms | Ex 8 |
| L | Exam twist | reading the return value precisely | Ex 9 |
Nine examples, twelve cells. Let's go.
Ex 1 — sort with ties, and the degenerate ranges (cells A, B)
Forecast: guess the descending result, and guess whether (b) crashes.
Part (a) — steps.
- Write the comparator
[](int a, int b){ return a > b; }. Why this step?sortcallscmp(a,b)and putsabeforebwhen it returnstrue. "a > bis true whenais bigger" ⇒ bigger elements come first ⇒ descending. The default<gives ascending. - Apply
sort(v.begin(), v.end(), cmp). Why this step? Random-access iterators fromstd::vectorlet introsort do its work (see Time complexity Big-O). - The two
1s are tied.std::sortis not guaranteed stable, so their relative order is unspecified — but since both are1, we cannot tell, and it does not matter. Why this step? Ties are a real corner: if you needed to preserve original order among equals, you'd usestable_sort.
Result: v = {9, 6, 5, 4, 3, 2, 1, 1}.
Part (b) — degenerate steps.
- Empty range:
first == last, so the "smart loop" runs zero iterations. No crash, vector stays{}. - Single element: nothing to compare against, loop body never swaps. Stays
{7}. Why this step? The half-open range[first, last)makes both these the natural base cases — never a specialif.
Ex 2 — find: present and absent (cell C)
Forecast: what iterator comes back in each case, and what index?
Steps.
auto it1 = find(v.begin(), v.end(), 30);Why this step?findis a linear scan returning an iterator to the first match.- Since
30is present,it1 != v.end(). Index= it1 - v.begin() = 2. Why this step? Subtractingbegin()from a found iterator gives the position — valid only for random-access iterators. auto it2 = find(v.begin(), v.end(), 99);Why this step? Now test the failure path — the whole point of the scenario matrix is that this path exists.99is absent ⇒it2 == v.end(). Do not dereference it, and do not compare tonullptr. Why this step? The sentinel is the samelastyou passed in — this always exists, even for empty vectors.
Ex 3 — transform: in-place, then two-range (cells D, E)
Forecast: will overwriting the source corrupt later elements?
Part (a) — steps.
transform(v.begin(), v.end(), v.begin(), [](int x){ return x*x*x; });Why this step? The 3rd argument isdest. Settingdest == firstmeans "write results back onto the source."- Is that safe?
transformreads elementi, computes, writes elementi— all before touchingi+1. No element is read after it's overwritten. Why this step? Aliasing is only dangerous when a write clobbers something a later read needs; here the read and write of the same slot are paired, so it's safe.
Result: v = {1, 8, 27, 64}.
Part (b) — two-range steps.
- Make a destination with room:
vector<int> net(3);Why this step? Unlikecopymistakes later, we pre-size so writing tonet.begin()is legal. transform(p.begin(), p.end(), d.begin(), net.begin(), [](int a, int b){ return a - b; });Why this step? The binary form walks both ranges in lockstep, callingg(p[i], d[i]). The 2nd range only needs afirst(d.begin()) — its length is assumed ≥ the first range's.
Result: net = {90, 175, 270}.
Ex 4 — accumulate: the int-vs-double type trap (cell F)
Forecast: guess both sums — one of them is wrong on purpose.
Steps.
accumulate(v.begin(), v.end(), 0)— the accumulator type is the type ofinit, hereint. Why this step? The parent note's rule: result type =inittype. This is the whole trap.- Trace the fold with truncation to
intat each+: ; truncated to ; ; . Why this step? Each partial sum is stored asint, so fractional parts vanish at every step, not just at the end. - Result of (a) =
6. Now (b):accumulate(v.begin(), v.end(), 0.0)keepsdoubleaccuracy. Why this step? Passing0.0makes the accumulator adouble; the true sum survives. - True sum .
Ex 5 — accumulate as a non-sum fold: identity matters (cell G)
Forecast: guess (a), then predict the disaster in (b).
Steps.
accumulate(v.begin(), v.end(), 1, [](int a, int b){ return a*b; });Why this step? For multiplication the identity element (the value that changes nothing) is1, since . Soinitmust be1.- Fold: . Answer
24. - (b) With
init = 0: . Answer0— always zero! Why this step?0is the absorbing element of multiplication, not the identity. Pick the wronginitand the answer is meaningless. - (c)
accumulate(w.begin(), w.end(), string(""), [](string a, string b){ return a + b; });Why this step? The identity for string concatenation is the empty string"". Left-fold:"" + "a" + "b" + "c" = "abc".
Recall The identity rule
Every fold needs init = the operation's identity: +→0, *→1, &&→true, ||→false, string +→"". Otherwise you shift or corrupt the result.
Ex 6 — copy with no room, and the back_inserter fix (cell H)
Forecast: what goes wrong in (a)?
Steps.
- (a)
vector<int> dst; copy(src.begin(), src.end(), dst.begin());Why this step? This looks right — we pointdestatdst.begin(). Butdsthas size0, sodst.begin() == dst.end(). There is no slot to assign into. - Result: undefined behaviour (writing past the end). It may crash, may silently corrupt memory. It does not grow
dst. Why this step?copyassigns; it never callspush_back. This is the corner the matrix demands. - (b)
copy(src.begin(), src.end(), back_inserter(dst));Why this step?back_inserter(dst)is an output iterator whose "assign" actually callsdst.push_back(...), sodstgrows to hold each element.
Result: dst = {5, 6, 7}.
Ex 7 — all_of / any_of: empty range and short-circuit (cells I, J)

Forecast: guess the two booleans in (a) before reading on.
Part (a) — steps.
all_ofon{}: there is no counterexample to "every element is even," so by logic is true. Why this step? This is vacuous truth. It also matches AND-folding starting fromtrue: fold over nothing leavestrue.any_ofon{}: there is no witness to "some element is even," so it is false. Why this step? Matches OR-folding starting fromfalse.
Part (b) — short-circuit steps.
all_ofscans left to right, checkingisEven. Look at the figure:2✓,4✓, then5✗. Why this step?all_ofreturnsfalsethe instant one element fails — it does not need to see6or8.- It stops at index
2(the5). Returnsfalse. Why this step? Short-circuiting is a performance guarantee: worst case , but it quits early when it can.
Ex 8 — Word problem: combine algorithms (cell K)
Forecast: predict all three answers.
Steps.
- Total:
accumulate(s.begin(), s.end(), 0)→ sums asint. All data are ints, so0is the right identity. Why this step? Sum is a left fold with+; identity0. . - Ever closed?
any_of(s.begin(), s.end(), [](int x){ return x == 0; }). Why this step? "Ever closed" = "is there at least one zero" = existential =any_of. There are two zeros, sotrue. - Sorted high-to-low:
sort(s.begin(), s.end(), greater<int>()). Why this step?greater<int>()is a functor equivalent toa > b⇒ descending. Zeros sort to the bottom. Result{340, 275, 120, 0, 0}.
Ex 9 — Exam twist: read the return value exactly (cell L)
Forecast: the trick is what transform returns, not what it writes.
Steps.
transformwritesx+10for each of the 3 elements:out = {11, 12, 13}. Why this step? Straightforward unary map.transformreturns the iterator one past the last written element — hereout.begin() + 3. Why this step? This is the consistent[first, last)philosophy: the return is exactly where the next algorithm would continue writing.ret - out.begin() = 3. Why this step? The distance from the start to "one past last written" equals the count written.
Recall One-line recall for every cell
sort ties :::: order among equals unspecified (use stable_sort if it matters)
find absent :::: returns last, never nullptr
transform in-place :::: safe because read+write of slot i happen before i+1
accumulate int init over doubles :::: truncates at every step → wrong
accumulate identity :::: +→0, *→1, &&→true, ||→false, string→""
copy into empty :::: UB; use back_inserter
all_of empty / any_of empty :::: true / false
algorithm return values :::: transform/copy return "one past last written"
Flashcards
sort on an empty or single-element range does what?
accumulate({1.5,2.5,3.0}, 0) gives what and why?
6, because the int init truncates every partial sum.Correct product init and why?
1 — the identity of multiplication; 0 is absorbing and gives always-zero.Fix for copy into an empty vector?
back_inserter(dst) so each assign calls push_back.What does transform / copy return?
dest).