Learn C++ STL

Lesson 1 of 6 ¡ What the STL Is, and Why It Changes How You Write C++

Module 0 ¡ What the STL Is, and Why It Changes How You Write C++

The STL in One Picture: Four Kinds of Thing

FreeReading

In this lesson

  • Say what the STL is in one sentence.
  • Name its four kinds of thing and give one example of each.
  • Explain what the STL is not, so you know where to look for the rest.

Maria has a C program that keeps a shop's price list. It has a hand-written growing array, a hand-written linked list and a hand-written sort. It is about 300 lines long, and it has two bugs she has not found yet.

The same program in C++ with the STL is about 40 lines. The two bugs are gone. Nobody fixed them: the lines that held them no longer exist, because the growing array, the list and the sort now come from the library.

Maria asks the question this whole module answers: "But why should I trust code I did not write?" Before we answer it, you need the map. This lesson is the map.

What the STL is, in one sentence

The STL (Standard Template Library) is the part of the C++ standard library that provides containers, iterators, algorithms and function objects, built to fit together.

The standard library is the code that ships with every C++ compiler, the way printf and qsort ship with every C compiler. You do not download it. You include a header and use it.

The design came from Alexander Stepanov, working with Meng Lee at Hewlett-Packard. The C++ standards committee voted it into the draft standard in 1994. It has been part of every C++ standard since the first one, C++98. The word "Template" in the name means the same code works for any type of element. Module 1 shows you what that means in practice.

So the STL is not a separate product. It is a set of headers, such as <vector> and <algorithm>, that your compiler already has.

The picture: four kinds of thing

Everything in this track belongs to one of four kinds. Learn this picture now. Every later module adds detail to one of its boxes, and you will see it again at the top of each one.

The four kinds of thing in the STL and how they connect Containers hold the data vector, map, set Iterators point into a container begin(), end() Algorithms do the work sort, find, count Function objects say how greater<int>, a lambda hands out a range passed in An algorithm never sees the container. It only sees two iterators.
Figure 1. The four kinds. Containers hold, iterators point, algorithms work, function objects say how. This picture opens every module of the track.

Read the arrows from left to right. A container hands out iterators. Two iterators mark a stretch of elements, called a range. An algorithm works on that range, and a function object, when you pass one, tells the algorithm how to compare or what to do.

So the four kinds are not four piles of features. They are four parts of one machine, and the iterator is the part in the middle that lets the other three meet.

Reading one STL line

std::sort(prices.begin(), prices.end(), std::greater<int>());
  • std:: says the name comes from the standard library. Every STL name lives there.
  • sort is the algorithm, the work to be done.
  • prices is the container, the thing holding the numbers.
  • prices.begin() and prices.end() are two iterators: where the range starts, and just past where it stops.
  • std::greater<int>() is a function object: "put the bigger one first".

Containers hold things

A container is an object that holds a collection of elements and manages their memory for you. You add to it and remove from it. It grows and shrinks on its own, so you never call malloc or free for it.

std::vector is the one you will use most. It is a growing array: the elements sit next to each other in memory, just like a C array, and you can still write v[0].

Example 1: a container

The smallest useful STL program. Three scores go in, and the vector keeps count.

#include <iostream>
#include <vector>

int main()
{
    std::vector<int> scores;
    scores.push_back(72);
    scores.push_back(45);
    scores.push_back(90);
    std::cout << "I hold " << scores.size() << " scores\n";
    std::cout << "The first is " << scores[0] << '\n';
}
I hold 3 scores
The first is 72

push_back adds one element at the end, and size() says how many there are. There is no capacity to track and nothing to free. If std::cout << is new to you, read it as printf for now; Module 1 teaches it properly.

Run in Compiler

Iterators point into containers

An iterator is an object that points at one element of a container and can move to the next one. If you know C pointers, you already know the idea: *it reads the element it points at, and ++it moves it forward.

Every container hands out two of them. begin() points at the first element. end() points one step past the last element, a position with nothing in it. Lesson 4 explains why that is the right choice.

Example 2: an iterator

The long type name is written out once so you can see it. From Module 1 on, auto writes it for you.

#include <iostream>
#include <vector>

int main()
{
    std::vector<int> scores = {72, 45, 90};
    std::vector<int>::iterator it = scores.begin();
    std::cout << *it << '\n';
    ++it;
    std::cout << *it << '\n';
}
72
45

The iterator started at 72, moved one step, and then read 45. So an iterator is a position you can read and move, which is exactly what a pointer into a C array is.

Run in Compiler

Algorithms work through iterators

An algorithm is a function template in <algorithm> (or <numeric>) that does one job on a range: sort it, search it, count in it, reverse it. There are more than a hundred of them.

Here is the important part. An algorithm takes two iterators, not a container. It never asks "is this a vector?". It only walks from the first iterator to the second. That is why one std::sort works on a vector, on a plain array and on containers that were written years after it.

Example 3: an algorithm

Two algorithms on one vector: std::sort puts it in order, and std::count counts one value.

#include <algorithm>
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> scores = {72, 45, 90, 45, 61};
    std::sort(scores.begin(), scores.end());
    std::cout << "sorted:";
    for (int s : scores) std::cout << ' ' << s;
    std::cout << '\n';
    std::cout << "45 appears " << std::count(scores.begin(), scores.end(), 45) << " times\n";
}
sorted: 45 45 61 72 90
45 appears 2 times

The line for (int s : scores) means "for each element s of scores". It is the range-for, and Module 1 gives it a lesson of its own. Notice that neither algorithm was told the size: the two iterators carry that.

Run in Compiler

Function objects tell an algorithm how

A function object (also called a functor) is anything you can call like a function. You pass one to an algorithm to change how it does its job. std::sort puts the smaller element first by default; give it std::greater<int>() and it puts the bigger one first.

In C you did the same thing with a function pointer, the comparator you pass to qsort. A function object does that job, with the type checked by the compiler. The most common kind today is a lambda, a small function written right where you need it. Module 12 teaches lambdas; here you only need to recognise one.

Example 4: a function object

The same sort, twice. The second call gets a function object, and the order flips.

#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> scores = {72, 45, 90, 61};
    std::sort(scores.begin(), scores.end());
    std::cout << "smallest first: " << scores[0] << '\n';
    std::sort(scores.begin(), scores.end(), std::greater<int>());
    std::cout << "biggest first:  " << scores[0] << '\n';
}
smallest first: 45
biggest first:  90

std::greater<int> lives in <functional>. It answers one question for any two ints: "is the first one bigger?". So the sort did not change; only the rule it compares with did.

Run in Compiler

All four in one program

Here is the core of Maria's rewrite: her price list, sorted from the most expensive down, with the top three printed. Point at each line and name its kind before you read the note under it.

Example 5: Maria's price list
#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> prices = {450, 1200, 80, 990, 300, 640};
    std::sort(prices.begin(), prices.end(), std::greater<int>());

    std::cout << "Top three prices:\n";
    for (auto it = prices.begin(); it != prices.begin() + 3; ++it) {
        std::cout << "  " << *it << '\n';
    }
    std::cout << "Items over 500: "
              << std::count_if(prices.begin(), prices.end(), [](int p) { return p > 500; }) << '\n';
}
Top three prices:
  1200
  990
  640
Items over 500: 3

The vector is the container. sort and count_if are algorithms. begin() and it are iterators, and begin() + 3 is "three steps in". std::greater<int>() and the lambda [](int p) { return p > 500; } are function objects. Fifteen lines of code, and not one of them manages memory.

Run in Compiler

What the STL is not

Knowing the edges saves you a long search. Three things people expect to find in the STL are not there.

  • Not a GUI library. There are no windows, buttons or images. Programs with a window use a separate library such as Qt.
  • Not a network library. Standard C++17 and C++20 have no sockets and no HTTP. A server uses the operating system's calls or a library such as Boost.Asio.
  • Not the whole standard library. Input and output (<iostream>), time (<chrono>), threads and files are standard library too. They are not the container, iterator and algorithm design this track is about.

One honest note on the name. Many programmers say "the STL" when they mean the whole standard library, and you will hear it used both ways. This track uses it for the four kinds above, plus std::string, which behaves like a container of characters.

Where this is used

  • LLVM. The compiler toolkit behind Clang uses std::vector and std::sort throughout. It also has its own containers, such as SmallVector, built with the same begin() and end(), so the standard algorithms work on them unchanged.
  • Chromium. The browser behind Chrome and Edge uses the standard containers. Where it needed something different, its base library adds containers such as base::flat_map with the same iterator interface.
  • Unreal Engine, the counter-example. The game engine ships its own containers (TArray, TMap, TSet) and its code uses them instead of the STL's. Even so, a TArray has begin() and end(), so the four-kinds picture still describes it.
  • Programming contests. ICPC and Codeforces compile C++ with the full standard library. A contestant sorts a million numbers with one call to std::sort and spends the saved time on the problem.

Common mistakes

1. Looking for sort on the container.

std::vector<int> v = {3, 1, 2};
v.sort();

GCC 12 says error: 'class std::vector<int>' has no member named 'sort'. Algorithms are not members of the container; they stand apart and take iterators. Write std::sort(v.begin(), v.end());. You will make this mistake because most languages you meet later put sort on the list itself.

2. Forgetting the algorithm's header.

#include <iostream>
#include <vector>

int main() {
    std::vector<int> v = {3, 1, 2};
    std::sort(v.begin(), v.end());
}

On the Playground, GCC 12 says error: 'sort' is not a member of 'std'; did you mean 'qsort'?. Containers have their own headers, and algorithms live in <algorithm>. Add #include <algorithm>. Some other compilers happen to compile this anyway, which is why it surprises people when it stops working.

3. Choosing the container before knowing the question.

Kenji's first question is always "which container is fastest?". No container is fastest at everything: one is fast at adding to the end, another at finding a name, another at keeping things in order. Lesson 3 is the table that answers "fastest at what?". Decide what you will ask of the data first, then pick.

Brain teaser

Four short descriptions. Which of the four kinds is each one: container, iterator, algorithm or function object?

  1. It keeps names in alphabetical order and tells you quickly whether a name is there.
  2. It stands on the third book of a shelf and can step to the fourth.
  3. Give it any stretch of elements and it turns that stretch back to front.
  4. Show it two prices and it answers one question: should the first one come before the second?

Ask of each one: does it hold, point, work or decide? Only one of them owns any elements.

Exercise 1Easy

Sort these eight names into the four kinds: std::vector, std::find, begin(), std::greater<int>, std::map, std::reverse, end(), std::less<int>.

Check yourself. You should have two names in each kind. If one box has three, look again at Figure 1 and at the syntax card above.

Exercise 2Medium

Maria's C program had three hand-written parts: a growing array, a linked list and a sort. For each one, write the STL name that replaces it and the kind it belongs to.

Rules. One line per part. You may use Lesson 3's container list if you have read it; otherwise, guess the list's name from its job and check it later.

Check yourself. Two of your three answers are containers and one is an algorithm. The algorithm needs a container to work on, but not a particular one.

Exercise 3Hard

Write one paragraph that answers this: std::sort was written more than ten years before LLVM's SmallVector existed, yet it sorts a SmallVector correctly. How can a function work on a container its author never saw?

Rules. Use the words "iterator" and "range". Do not use the word "magic".

Check yourself. A good answer says that sort never sees the container, only two iterators. Any container that hands out the right kind of iterator can be sorted. Figure 1's dashed box is the whole answer.

Common doubts

  • Is the STL a separate thing I have to install?

    No. It is part of the standard library that comes with every C++ compiler, including the Playground's GCC 12. You include a header such as <vector> and it is there.

  • Do I need to know C++ classes before learning the STL?

    No. You need to use classes, not write them, and using one looks like v.push_back(3). Module 1 teaches the few C++ pieces this track relies on. Writing your own classes and templates belongs to a C++ track.

  • Is code that uses the STL slower than hand-written C?

    Usually not, and sometimes faster: std::sort is often faster than qsort, because the compiler can inline the comparison. Lesson 2 is honest about where the STL does cost you something.

  • Why is it called a "template" library?

    Because each container and algorithm is written once as a template, a pattern the compiler fills in with your type. std::vector<int> and std::vector<double> are two versions it writes for you. Module 1 has a lesson on this idea.

Key takeaways

  • The STL is the part of the C++ standard library that gives containers, iterators, algorithms and function objects.
  • Containers hold, iterators point, algorithms work, function objects say how.
  • An algorithm takes two iterators, never a container, so one algorithm works on many containers.
  • The STL is not a GUI, not a network library and not the whole standard library.
  • Figure 1 is the map: every later module fills in one of its boxes.

Next you will see why it is worth learning. Three programs are written twice, once in C and once with the STL, beside the costs nobody puts on the poster.

End of lesson 1

Mark it done, and your progress moves with you.

Next: Why It Matters: Fewer Lines, Fewer Bugs, Known Costs