Container Operations in C++
Keeping the container versions separate means a program can use a shorthand that accepts a whole container while the standard namespace keeps its original, sequence-based meaning.
A Tempting Assumption About Algorithms
Most people first meet containers through operations that visibly change them, such as adding an item to the end of a list or removing one from the middle. It is natural to carry that experience over to the standard library. If an algorithm is handed a container, the reasoning goes, it should be able to shrink it, grow it, or rearrange whatever it holds. The source material corrects this belief directly. Standard library algorithms can change the values of the elements they work on, but no algorithm adds or subtracts elements of a container. Before reading on, try to decide for yourself which side of that line a few familiar operations fall on.
What do you think happens?
Given a std::vector<int> holding {4, 1, 4, 2}, does calling std::replace on it with the value 4 change the number of elements in the vector?
Reveal answer
No, it only overwrites the values of matching elements.
std::replace changes every matching element to a new value. Algorithms can change element values, but no algorithm adds or subtracts elements of a container. The element count stays at four.
Where Standard Algorithms Live
A standard library algorithm is a reusable operation that answers a question about, or performs a transformation on, a group of elements. The standard library provides dozens of these. They are defined in the namespace std and presented in the algorithm header. The standard library's algorithms are designed to be used in preference to hand-written loops over the same group, because they tend to be more carefully designed, specified, and implemented than the average loop written from scratch.
Every standard library algorithm receives its input as a sequence. Two facts about the call matter from the start: the algorithm comes from the algorithm header and lives in namespace std, and its inputs are positions that describe a run of elements rather than the container object itself.
Reading a Half-Open Sequence
A sequence is described by two positions. The first marks where the sequence starts, and the second marks where it ends. The source writes a half-open sequence from b to e as $[b : e)$. The square bracket on the left means the start position is included. The parenthesis on the right means the end position is excluded. The sequence therefore covers the elements from b up to, but not including, e.
The end position is the stopping marker of the sequence. Because it is excluded, the number of elements in the sequence is simply the distance between the two positions, and an empty range needs no special case: when the two positions are equal, the distance is zero. The same convention explains why an algorithm that searches a sequence and finds nothing conventionally returns the end position itself, since there is no element inside the range for it to point at.
Covering elements with a half-open sequence
A vector holds the values 10, 20, 30, 40. Which elements does the sequence [begin + 1 : end) cover, and how many are there?
- 1Identify the start position
begin + 1 points at the second element, the value 20. The start position is included.
- 2Identify the end position
end points one past the last element. It is excluded, so the value 40 is still covered but nothing after it is.
- 3List the covered elements
The covered elements are 20, 30 and 40. The value 10 at the first position is outside the sequence.
- 4Count using the distance
The count is the distance between the positions, which is 4 minus 1, giving 3 elements.
The sequence covers 20, 30 and 40, which is three elements.
Algorithms That Change Values
Several representative algorithms follow the same pattern: a sequence is supplied as input, and the algorithm answers a question or performs an operation over the elements in that range. The count algorithm returns how many elements in the sequence equal a value. The replace algorithm changes every matching element to a new value. Algorithms with the suffix _if do the same jobs but use a test function instead of a fixed value to decide which elements qualify. The following program uses count and replace on a vector. Trace it before reading the output.
count of 4: 2
size after replace: 4
9 1 9 2 The output confirms the prediction. The two matching 4s are now 9s, the vector still holds four elements, and the 1 and 2 are untouched. The algorithm wrote new values into existing positions, which is possible because each position leads to an element that can be assigned to. Nothing in that process created or destroyed an element.
Why Algorithms Cannot Change the Count
The rule that algorithms never add or subtract elements follows from how sequences are defined. A sequence is given by two positions and nothing else. The algorithm therefore sees a run of elements that it can read and, where the operation calls for it, overwrite. It never sees the container that owns those elements, so it has no handle through which to insert a new element or erase an old one. Changing how many elements exist requires operating on the container itself.
The diagram makes the missing link visible. The sequence covers elements that belong to the container, and the algorithm consumes the sequence. There is no edge from the algorithm to the container, which is exactly why an algorithm alone cannot change how many elements the container holds.
Adding and Removing Elements
If elements must be added or removed, the source gives two routes. One is to supply something that knows about the container, such as a back inserter, which lets an algorithm that writes to an output position add elements to a container it is connected to. The other is to call operations directly on the container, such as push_back() to add an element at the end or erase() to remove one. Both routes bring the container into the work, which the bare algorithm does not do. The next program uses the second route.
after push_back: 5
after erase: 4
9 9 2 7 Notice that push_back() and erase() are member operations called on the vector itself. The count changes only because the container was asked to change. For the output-iterator route, the same idea appears in a copy call that writes through a back inserter, such as std::copy(src.begin(), src.end(), std::back_inserter(out)). The copy algorithm still only walks a sequence and writes to positions; the back inserter is what connects those writes to the container and lets it grow.
Where the Container Versions Live
Most of the time, an algorithm is applied to a sequence that is the contents of a container. The source notes that the sequence concept is general, but in practice the sequence usually comes from a container. To make that common case easier, container versions of sort() and other algorithms can be provided. The source places them in their own namespace, called Estd, which stands for extended std. The purpose of this separation is to avoid interfering with other programmers' uses of namespace std.
Keeping the container versions separate means a program can use a shorthand that accepts a whole container while the standard namespace keeps its original, sequence-based meaning. The shorthand is a convenience layer on top of the same underlying idea of sequences defined by a pair of positions.
Mistakes Learners Make With Algorithms
Common mistake Expecting an algorithm to grow or shrink the container it touches
What it looks likeCalling std::replace on a vector and expecting its size to change when new values are written.
Why it failsNo algorithm adds or subtracts elements. Algorithms only read and, where the operation calls for it, overwrite values inside the sequence.
Do this insteadUse container operations such as push_back() or erase() on the container when the count must change, or an inserter connected to the container.
Common mistake Treating the end position as the last element
What it looks likeAssuming that for a vector of four elements, the position e refers to the value 40.
Why it failsThe sequence excludes its end position, so e points one past the last element.
Do this insteadRead $[b : e)$ as covering b up to but not including e. The number of elements is the distance between the two positions.
Common mistake Assuming the algorithm header or the sequence is enough to modify the container
Why it failsThe algorithm receives only positions. It never sees the owning container, so it has no way to insert or erase elements.
Value Changes Versus Count Changes
| Operation kind | Examples from the source | Changes values? | Changes element count? | What supplies the count change |
|---|---|---|---|---|
| Search or count | find, count, find_if, count_if | No | No | Not needed |
| Overwrite in place | replace, replace_if, sort | Yes | No | Not needed |
| Copy or merge into an output | copy, merge | Writes to output positions | Only with a back inserter or container operation | Back inserter or container call |
| Direct container change | push_back, erase | Depends on use | Yes | The container operation itself |
Algorithms change values in place; changing the count always involves the container.
Predicting Before You Run
A vector holds {5, 3, 5, 8}. You call std::replace over the whole vector, replacing 5 with 0, and then call std::count over the whole vector looking for 0. Predict the count returned and the size of the vector afterwards, then state which operation, if any, would be needed to make the vector hold five elements.
Hints
- Ask whether replace can change the number of elements or only the values at existing positions.
- The sequence [b : e) covers every element of a whole-vector call, since b is begin and e is end.
- If the count must grow, think about
push_back()or an inserter connected to the container.
Key Takeaways
- Standard library algorithms are defined in namespace std, presented in the algorithm header, and take sequences as their inputs.
- A half-open sequence $[b : e)$ includes b and excludes e, so its element count is the distance between the two positions.
- Algorithms such as replace and sort can change element values, but no algorithm adds or subtracts elements; changing the count requires container operations like
push_back()anderase(), or an inserter connected to the container. - Container versions of algorithms are placed in the separate Estd namespace (extended std), so the std sequence-based meaning is preserved.