Concepts / The list Container

The list Container

The standard containers share operation notation, so the same name is written the same way across containers such as vector and list.

  • Programming

Carrying Vocabulary from One Container to Another

Suppose you already know how to add an element to the end of one kind of container, and now you need to use a different kind. Do you have to learn a completely new vocabulary for the second one, or does the vocabulary carry over? The standard library answers this question in a specific way, and the answer shapes how you read almost any container code you meet.

The short answer is that the vocabulary carries over. The standard containers, such as vector and list, are built so that their basic operations look alike when you write them down. A call that works on one container is written with the same name and the same shape of syntax on another, so you can read the code without first checking which container you are looking at. This shared notation is deliberate.

Shared Names and Shared Meanings

Definition

The standard containers share operation notation, so the same name is written the same way across containers such as vector and list. Sharing a notation is, however, only half of the design. The meaning of each shared operation is also equivalent across containers.

An operation named for finding the start of a sequence means the start of the sequence whether the container underneath is a vector, a list, or another container type. What differs between containers lies in how each one stores its elements, not in what the basic operations are supposed to mean. So when you see begin(), push_back(), size(), or end(), you can rely on the idea behind the name even before you know the container's internal layout.

begin()first elementbegin()first elementend()one past the lastend()one past the lastpush_back()adds to the endpush_back()adds to the endsize()number of elementssize()number of elements
Which operation names appear in both containers, and do they keep the same meaning? The same four names appear in the vector column and in the list column; the name and its meaning carry across, while internal storage is not shown.

Reading begin() and end() as Boundaries

begin() and end() are best read together. begin() gives an iterator to the first element, and end() gives an iterator to the position one beyond the last element. Together they mark the boundaries of the whole sequence: the first position you can visit, and the position where a walk through the elements stops.

Notice that end() is not an element you can read. It is a marker that sits past the final element. Because it works as a stopping point for a walk through the container, it is described as one-beyond-the-last rather than as the last element. Picture the elements as a row of slots: begin() points at the first slot, and end() points at the empty slot just after the final one.

begin()first position10element 020element 130element 2end()one past the last, noelementpoints tofollows the final element
Where exactly do begin() and end() point? begin() marks the first element, and end() marks the position one past the last element, which holds no element.

Tracing push_back() and size() Step by Step

push_back() adds an element to the end of a container, and size() returns the number of elements. Together these two operations let you watch a container change. Each push_back() call grows the container by one element at its end, and size() reports the new count afterward.

cpp
Output
0
3
Walk through it
  1. 1
    The container starts empty, so size() reports 0.
  2. 2
    Each push_back call adds one element at the end, in the order the calls are made.
  3. 3
    After three calls the container holds 10, 20, and 30, and size() describes that count as 3.
Notes on the code
  • std::list<int> scores creates an empty container of integers
  • size() reports 0 before anything is added
  • push_back(30) places the third element after the existing ones
  • size() reports 3 after the three additions
Before
scores holds: 10, 20, 30

Three elements, size() reports 3

After
scores.push_back(40); scores holds: 10, 20, 30, 40

Four elements, size() reports 4

10102020303040newly added at the end
After push_back(40) is called, the container holds the new element after the existing ones. The new element appears at the end, and the count rises from 3 to 4.

What do you think happens?

If the container holds 10, 20, 30 and you call push_back(40), where does 40 appear, and what does size() report afterward?

  • 40 appears at the front and size() reports 4

  • 40 appears at the end and size() reports 4

  • 40 replaces 30 and size() reports 3

Reveal answer
Answer:

40 appears at the end and size() reports 4

push_back() adds an element to the end of the container, so the existing elements keep their order and the new one follows them. size() returns the number of elements, which goes from 3 to 4.

When a Shared Operation Applies

Uniformity has conditions. An operation applies where it makes sense and can be efficiently implemented. This means that a shared name does not guarantee identical cost everywhere. The name tells you what the operation is meant to do; it does not promise that every container will do it at the same price.

Read this way, the conditions for a basic operation come down to two questions. First, does the operation make sense for this container? Second, can it be implemented efficiently for this container? When both answers are yes, the call reads the same as it would on any other standard container. When either answer is no, the shared name still tells you the intended meaning, but you should not assume the operation is equally suited to that container.

Mistakes Readers Make With Shared Operations

  • Common mistake

    Treating end() as the last element

    What it looks like

    Reading the value at end() and expecting the final element of the container

    Why it fails

    end() marks the position one beyond the last element, so it is a stopping marker rather than a stored element.

    Do this instead

    Use begin() as the first position and end() as the stopping point for a walk through the elements.

  • Common mistake

    Assuming a shared name means identical cost

    What it looks like

    Choosing a container only because the same operation name appears in both

    Why it fails

    A shared name guarantees the meaning, but uniformity has conditions and an operation is not guaranteed to be efficient on every container.

    Do this instead

    Check whether the operation is expected to apply efficiently to the specific container before relying on it.

  • Common mistake

    Expecting push_back() to insert at the front

    What it looks like

    Calling push_back(40) and assuming 40 becomes the first element

    Why it fails

    push_back() adds an element to the end of the container.

    Do this instead

    Read push_back() as appending after the current final element.

  • Common mistake

    Assuming the internal storage is shared when only the names are shared

Practice Reading a Container's Operations

EASY

A list container holds the values 5, 8, and 12 in that order. Predict what begin() points to, what end() points past, and what size() reports after one more push_back(7) call. Explain why end() does not hold a value.

Hints
  • begin() points to the first element, which is 5.
  • end() points one position past the last element that exists at the time you ask.
  • After push_back(7), the new element is last and the count increases by one.

Key Takeaways

  • The standard containers share operation names and notation, so a call written for one container reads the same way on another.
  • The shared names also carry equivalent meanings: begin() gives the first element, end() gives the position one beyond the last element, push_back() adds at the end, and size() returns the number of elements.
  • end() is a marker, not a stored element, which is why it is read as one-beyond-the-last.
  • A shared name does not guarantee identical cost; a basic operation applies where it makes sense and can be efficiently implemented.

Related concepts

More concepts starting with T