Rainer Grimm - The C++ Standard Library (2d Ed., Including C++ 17) - Leanpub (2018)
Rainer Grimm - The C++ Standard Library (2d Ed., Including C++ 17) - Leanpub (2018)
Rainer Grimm - The C++ Standard Library (2d Ed., Including C++ 17) - Leanpub (2018)
Rainer Grimm
This book is for sale at http://leanpub.com/cpplibrary
This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing
process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and
many iterations to get reader feedback, pivot until you have the right book and build traction once
you do.
Reader Testimonials . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . i
English Edition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . i
German Edition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . i
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iii
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iii
Conventions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iii
Source Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iv
Source Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iv
Value versus Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iv
Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . v
Further Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . v
C++ versus C++11 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . v
2. Utilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Useful Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Adaptors for Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Pairs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Tuples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Reference Wrappers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Smart Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Type Traits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Time Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
std::any, std::optional, and std::variant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
Compare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
4. Sequential Containers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
Vectors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
Deques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
Forward Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
5. Associative Container . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
Ordered Associative Container . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
Unordered Associative Container . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
7. Iterators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
Categories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
Iterator Creation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
Useful Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
Adaptors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
8. Callable Units . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
Function Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
Lambda Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
9. Algorithms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
Conventions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
Iterators are the glue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
Sequential, parallel, or parallel execution with vectorisation . . . . . . . . . . . . . . . . . . . 94
for_each . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
Non-Modifying Algorithms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
Modifying Algorithms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
Partition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
Sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
Binary Search . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
Merge Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
Heaps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
Min and Max . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
Permutations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
CONTENTS
Numeric . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
Reader Testimonials
English Edition
Rick Audet
Senior Engineer, Dolby Laboratories
German Edition
Odin Holmes
CEO/CTO at Auto-Intern GmbH
”Das Buch beinhaltet, wie der Name schon sagt, eine recht
ausführliche Beschreibung der STL. Dabei merkt Mann deutlich dass
der Autor auch selber auf hohem Niveau programmiert. Es gibt oft
den ein oder andere Tipp oder Wink in die richtige Richtung die
bei Bücher von Berufsautoren oft fehlen. Z.B. die Aussage dass
std::vector für 95% aller Fälle die beste Wahl ist oder dass
std::async meistens die erste Wahl sein sollte lenkt der Leser
geschickt in die richtige Richtung.
Auch die Auswahl an Komponente aus der STL ist sehr gut getroffen
(keiner kann in ein kürzen Buch die ganze STL beschreiben). Oft
sehe ich, vor allem in Deutschsprachige Literatur, dass die Auswahl
eher auf Komponente trifft die leicht zu beschreiben sind und nicht
auf die Nützlichen. Eine gute und dennoch kürze Beschreibung vom
std::regex z.B. ist weiß Gott nicht einfach aber in diesem Fall ist
es der Autor sehr gelungen.”
Ramon Wartala
Director Technology at Performance Media GmbH
Reader Testimonials ii
”Die 215 Seiten plus Index des ’C++ kurz & gut’ vom Autor Rainer Grimm
stellen ein gelungene Destillat viel umfangreicherer Texte zum Thema
da. So nimmt das Kapitel über die klassischen Algorithmen der
Standardbibliothek ganze 131 Seiten ein. Selbst kurze Beispiele für die
Anwendung der wichtigsten Bestandteile der Standardbibliothek passen
eng gedruckt in das schmale Büchlein. Auch wenn heute Tools wie Dash
oder entsprechend ausgestattete IDEs, mehr und mehr den Platz derartiger
Desktop-Referenzen einnehmen, ist das ’kurz & gut’ zu C++
Standardbibliothek ein leichter und mobiler Begleiter für jeden C++
Entwickler. Und als Kindle Version um so bequemer mitzunehmen.”
Introduction
The C++ Standard Library is a quick reference to the standard library of the current C++ standard
C++17 ISO/IEC 14882:20171 . C++17 has more than 1600 pages and it next big C++ standard after
C++14. C++14 is a small addition to C++11. C++11 had more than 1,300 pages and was published
2011. That was 13 years after the first and only C++ standard C++98. Of course, there is also C++03,
published in 2003. But C++03 is considered a bug fix release.
The goal of this quick reference is to provide a concise reference of the C++ standard library. This
book assumes that you are familiar with C++. If so you will get the most benefit out of this book.
If C++ is new to you, you should start with a textbook about core C++. Once you have mastered a
textbook about the core language, you can make your next big step by reading this book. To make
your job easier, I have provided a lot of short code snippets to connect theory and practice.
Index
The book should be a reference for C++, and should, therefore, have an index. Leanpub does not
support the creation of an index. So I’ve made it based on regular expressions, naming conventions,
a lot of python magic - don’t blame me - and a long, long table which I had to split for each page.
Here is the problem. The index is only fully available in the pdf format of the book.
Conventions
I promise only a few conventions.
Special Fonts
Italic
I use Italic if something is essential.
Monospace
I use Monospace for code, instructions, keywords and names of types, variables, functions and
classes.
1 https://www.iso.org/standard/68564.html
Introduction iv
Special Boxes
I use boxes for unique information, tips, and warning.
Information headline
Information text.
Tip headline
Tip description.
Warning headline
Warning description.
Source Examples
I dislike using directives and declarations because they hide the namespace of the library functions,
but because of the limited length of a page, I have to use them from time to time. I use them in such
a way that the origin can always be deduced from the using directive (using namespace std;) or
the using declaration (using std::cout;).
Only header files of the featured functionality are shown in the code snippets. True or false is
displayed in the output code snippets for boolean values; and
std::boolalpha is not used.
Source Code
To be concise, I only present short code snippets in this books. The name of the entire program is in
the first line of the code snippet.
Acknowledgements
At first Alexandra Follenius, who was the lector at O’Reilly for the German book C++ Standardbib-
liothek2 . You may have guessed that the German book is the ancestor of this book. For my book C++
Standardbibliothek Karsten Ahnert, Guntram Berti, Dmitry Ganyushin, Sven Johannsen, Torsten
Robitzki, Bart Vandewoestyne, and Felix Winter were very valuable proofreaders. A lot of thanks to
all of them.
For translating this book to English, I started a request in my English blog: www.ModernesCpp.com3 .
I received a much higher response than I expected. Special thanks to all of you, including my son
Marius, who was the first proofreader.
Here are the names in alphabetic order: Mahesh Attarde, Rick Audet, Pete Barrow, Michael Ben-
David, Dave Burns, Alvaro Fernandez, George Haake, Clare Macrae, Arne Mertz, Ian Reeve, Jason
Turner, Bart Vandewoestyne, Ivan Vergiliev, and Andrzej Warzynski.
Further Information
The idea of the book is quite easy to paraphrase: “What every professional C++ programmer should
know about the C++ standard library.” Because of this intention, I left a lot of answers unanswered;
therefore, I provide you with the links to the details at the very beginning of each new topic. The
link will be referring to the excellent online resource www.cppreference.com4 .
Surprisingly, C++11 feels like a new language: The pieces just fit together better than they
used to and I find a higher-level style of programming more natural than before and as
efficient as ever. (Bjarne Stroustrup, http://www.stroustrup.com/C++11FAQ.html)
Bjarne Stroustrup is right. C++11 feels like a new language, because it has a lot to offer in addition to
classic C++. This is true for the core language and is even more true for the improved and extended
standard library. The regular expression library for the manipulation of text, the type-traits library
to get, compare or manipulate types, the new random numbers library or the chrono library are all
new with C++11. But that’s not all. There are the smart pointers for automatic memory management
and the new containers std::array and std::tuple, which are further improved in C++14. C++11
is for the first time aware of multiple threads and offers a multithreading library.
2 http://shop.oreilly.com/product/9783955619688.do
3 http://www.modernescpp.com/index.php/do-you-wan-t-to-proofread-a-book
4 http://en.cppreference.com/w/
1. The Standard Library
The C++ standard library consists of many components. This chapter serves two purposes. It should
give you a quick overview of the components and a first idea how to use them.
The History
C++ and therefore the standard library have a long history. C++ started in the 1980s of the last
millennium and ended now in 2017. Anyone who knows about software development knows how
fast our domain evolves. So 30 years is a very long period. You may not be so astonished that
the first components of C++, like I/O streams, were designed with a different mindset than the
modern Standard Template Library (STL). This evolution in the area of software development in the
last 30 years, which you can observe in the C++ standard library, is also an evolution in the way
software problems are solved. C++ started as an object-oriented language, then incorporated generic
programming with the STL and now has adopted a lot of functional programming ideas.
C++ timeline
The first C++ standard library from 1998 had three components. Those were the previously
mentioned I/O streams, mainly for file handling, the string library and the Standard Template
Library. The Standard Template Library facilitates the transparent application of algorithms on
containers.
The history continues in the year 2005 with Technical Report 1 (TR1). The extension to the C++
library ISO/IEC TR 19768 was not an official standard, but almost all of the components became
part of C++11. These were, for example, the libraries for regular expressions, smart pointers, hash
tables, random numbers and time, based on the boost libraries (http://www.boost.org/).
The Standard Library 2
In addition to the standardisation of TR1, C++11 got one new component: the multithreading library.
C++14 was only a small update to the C++11 standard. Therefore only a few improvements to the
already existing libraries for smart pointers, tuples, type traits and multithreading were added.
What comes next in the C++ standard library? With C++17 and C++20 we will get two new
standards. C++17 is already done. C++17 includes libraries for the file system and the two new data
types std::any and std::optional. With C++20 we might get libraries for network programming;
with Concepts Lite we might get a type system for template parameters and better support for
multithreading.
Overview
As C++11 has a lot of libraries, it is often not so easy to find the convenient one for each use case.
Utilities
Utilities are libraries which have a general focus and therefore can be applied in many contexts.
Examples of utilities are functions to calculate the minimum or maximum of values or functions to
swap or move values.
Other utilities are std::function and std::bind. With std::bind you can easily create new
functions from existing ones. In order to bind them to a variable and invoke them later, you have
std::function.
With std::pair and it’s generalization std::tuple you can create heterogeneous pairs and tuples
of arbitrary length.
The reference wrappers std::ref and std::cref are pretty handy. One can use them to create a
reference wrapper for a variable, which for std::cref is const.
Of course, the highlights of the utilities are the smart pointers. They allow explicit automatic memory
management in C++. You can model the concept of explicit ownership with std::unique_ptr and
model shared ownership with std::shared_ptr. std::shared_ptr uses reference counting for taking
care of its resource. The third one, std::weak_ptr, helps to break the cyclic dependencies among
std::shared_ptrs, the classic problem of reference counting.
The type traits library is used to check, compare and manipulate type information at compile time.
The time library is an import addition of the new multithreading capabilities of C++. But it is also
quite handy to make performance measurements.
With std::any, std::optional, and std::variant, we get with C++17 three special datatypes that
can have any, an optional value, or a variant of values.
The Standard Library 3
The Standard Template Library (STL) consists of three components from a bird’s-eye view. Those
are containers, algorithms that run on the containers, and iterators that connect both of them. This
abstraction of generic programming enables you to combine algorithms and containers in a unique
way. The containers have only minimal requirements for their elements.
The C++ Standard Library has a rich collection of containers. From a bird’s eye we have sequential
and associative containers. Associative containers can be classified as ordered or unordered associate
containers.
Each of the sequential containers has a unique domain, but in 95 % of the use cases std::vector is the
right choice. std::vector can dynamically adjust its size, automatically manages its memory and
provides you with outstanding performance. In contrast, std::array is the only sequential container
that cannot adjust its size at runtime. It is optimised for minimal memory and performance overhead.
While std::vector is good at putting new elements at its end, you should use std::deque to put an
element also at the beginning. With std::list being a doubly-linked list and std::forward_list as
a singly linked list, we have two additional containers that are optimised for operations at arbitrary
positions in the container, with high performance.
Associative containers are containers of key-value pairs. They provide their values by their respec-
tive key. A typical use case for an associative container is a phone book, where you use the key family
name to retrieve the value phone number. C++ has eight different associative containers. On one
side there are the associative containers with ordered keys: std::set, std::map, std::multiset and
std::multimap. On the other side there are the unordered associative containers: std::unordered_-
set, std::unordered_map, std::unordered_multiset and std::unordered_multimap.
Let’s look first at the ordered associative containers. The difference between std::set and std::map
is that the former has no associated value. The difference between std::map and std::multimap
is, that the latter can have more than one identical key. This naming conventions also holds
for the unordered associative containers, which have a lot in common with the ordered ones.
The key difference is the performance. While the ordered associative containers have an access
time depending logarithmically on the number of elements, the unordered associative containers
allow constant access time. Therefore the access time of the unordered associative containers is
independent of their size. The same rule holds true for std::map as it does for std::vector. In 95 %
The Standard Library 4
of all use cases std::map should be your first choice for an associative container because the keys
are sorted.
Container adapters provide a simplified interface to the sequential containers. C++ has std::stack,
std::queue and std::priority_queue.
Iterators are the glue between the containers and the algorithms. The container creates them. As
generalised pointers, you can use them to iterate forward and backwards or to an arbitrary position
in the container. The type of iterator you get depends on the container. If you use an iterator adapter,
you can directly access a stream.
The STL gives you more than 100 algorithms. By specifying the execution policy, you can run most
of the algorithms sequential, parallel, or parallel and vectorised. Algorithms operate on elements or
a range of elements. Two iterators define a range. The first one defines the beginning, the second
one, called end iterator, defines the end of the range. It’s important to know that the end iterator
points to one element past the end of the range.
The algorithms can be used in a wide range of applications. You can find elements or count them,
find ranges, compare or transform them. There are algorithms to generate, replace or remove
elements from a container. Of course, you can sort, permute or partition a container or determine
the minimum or maximum element of it. A lot of algorithms can be further customised by callables
like functions, function objects or lambda-functions. The callables provide special criteria for search
or elements transformation. They highly increase the power of the algorithm.
Numeric
There are two libraries for numerics in C++: the random numbers library and the mathematical
functions, which C++ inherited from C.
The random numbers library consists of two parts. On one side there is the random number
generator, on the other hand the distribution of the generated random numbers. The random number
generator generates a stream of numbers between a minimum and a maximum value, which the
random number distribution maps onto the concrete distribution.
Because of C, C++ has a lot of mathematical standard functions. For example there are logarithmic,
exponential and trigonometric functions.
Text Processing
With strings and regular expressions, C++ has two powerful libraries to process text.
std::string possesses a rich collection of methods to analyze and modify its text. Because it has a lot
in common with a std::vector of characters, the algorithms of the STL can be used for std::string.
std::string is the successor of the C string but a lot easier and safer to use. C++ strings manage
their own memory.
The Standard Library 5
Multithreading
C++ gets with the 2011 published C++ standard a multithreading library. This library has basic
building blocks like atomic variables, threads, locks and condition variables. That’s the base on
which future C++ standards can build higher abstractions. But C++11 already knows tasks, which
provide a higher abstraction than the cited basic building blocks.
At a low level, C++11 provides for the first time a memory model and atomic variables. Both
components are the foundation for well-defined behaviour in multithreading programming.
A new thread in C++ will immediately start its work. It can run in the foreground or background
and gets its data by copy or reference.
The access of shared variables between threads has to be coordinated. This coordination can be done
in different ways with mutexes or locks. But often it’s totally sufficient to protect the initialisation
of the data as it will be immutable during its lifetime.
Declaring a variable as thread-local ensures that a thread get its own copy, so there is no conflict.
The Standard Library 6
Condition variables are a classic solution to implement sender-receiver workflows. The key idea is
that the sender notifies the receiver when it’s done with its work, so the receiver can start.
Tasks have a lot in common with threads. But while a programmer explicitly creates a thread, a task
will be implicitly created by the C++ runtime. Tasks are like data channels. The promise puts data
into the data channel, the future picks the value up. The data can be a value, an exception or simply
a notification.
Application of Libraries
To use a library in a file you have to perform three steps. At first, you have to include the header files
with the #include statement, so the compiler knows the names of the library. Because the names
of the C++ standard library are in the namespace std, you can use them in the second step fully
qualified or you have to import them in the global namespace. The third and final step is to specify
the libraries for the linker to get an executable. This third step is often not necessary. The three steps
are explained below.
#include <iostream>
#include <vector>
Usage of Namespaces
If you use qualified names, you have to use them exactly as defined. For each namespace you
must put the scope resolution operator ::. More libraries of the C++ standard library use nested
namespaces.
The Standard Library 7
#include <iostream>
#include <chrono>
...
std::cout << "Hello world:" << std::endl;
auto timeNow= std::chrono::system_clock::now();
You can use names in C++ with the using declaration and the using directive.
Using Declaration
A using declaration adds a name to the visibility scope, in which you applied the using declaration:
#include <iostream>
#include <chrono>
...
using std::cout;
using std::endl;
using std::chrono::system_clock;
...
cout << "Hello world:" << endl; // unqualified name
auto timeNow= now(); // unqualified name
• An ambiguous lookup and therefore a compiler error occur, if the same name was declared in
the same visibility scope.
• If the same name was declared in a surrounding visibility scope, it will be hidden by the using
declaration.
Using Directive
The using directive permits it to use all names of a namespace without qualification.
The Standard Library 8
#include <iostream>
#include <chrono>
...
using namespace std;
...
cout << "Hello world:" << endl; // unqualified name
auto timeNow= chrono::system_clock::now(); // partially qualified name
A using directive adds no name to the current visibility scope, it only makes the name accessible.
That implies:
• An ambiguous lookup and therefore a compiler error occur, if the same name was declared in
the same visibility scope.
• A name in the local namespace hides a name declared in a surrounding namespace.
• An ambiguous lookup and therefore a compiler error occurs if the same name get visible from
different namespaces or if a name in the namespace hides a name in the global scope.
Namespace Alias
A namespace alias defines a synonym for a namespace. It’s often convenient to use an alias for a
long namespace or nested namespaces:
#include <chrono>
...
namespace sysClock= std::chrono::system_clock;
auto nowFirst= sysClock::now();
auto nowSecond= std::chrono::system_clock::now();
Because of the namespace alias, you can address the now function qualified and with the alias. A
namespace alias must not hide a name.
Build an Executable
It is only seldom necessary to link explicitly against a library. That sentence is platform dependent.
For example, with the current g++ or clang++ compiler, you have to link against the pthread library
to get the multithreading functionality.
The Standard Library 9
Useful Functions
The many variations of the min, max and minmax functions are applicable on values and initialiser lists.
These functions need the header <algorithm>. Nearly the same holds for the functions std::move,
std::forward and std::swap. You can apply them to arbitrary values. These three functions are
defined in the header <utility>.
1 http://en.cppreference.com/w/cpp/algorithm/min
2 http://en.cppreference.com/w/cpp/algorithm/max
3 http://en.cppreference.com/w/cpp/algorithm/minmax
Utilities 11
// minMax.cpp
...
#include <algorithm>
...
using std::cout;
...
cout << std::min(2011, 2014); // 2011
cout << std::min({3, 1, 2011, 2014, -5}); // -5
cout << std::min(-10, -5, [](int a, int b)
{ return std::abs(a) < std::abs(b); }); // -5
The table provides an overview of the functions std::min, std::max and std::minmax
Function Description
min(a, b) Returns the minimal value of a and b.
min(a, b, comp) Returns the minimal value of a and b according to the predicate comp.
min(initialiser list) Returns the minimal value of the initialiser list.
min(initialiser list, comp) Returns the minimal value of the initialiser list according to the
predicate comp.
max(a, b) Returns the maximal value of a and b.
max(a, b, comp) Returns the maximal value of a and b according to the predicate comp.
max(initialiser list) Returns the maximal value of the initialiser list.
max(initialiser list, comp) Returns the maximal value of the initialiser list according to the
predicate comp.
minmax(a, b) Returns the minimal and maximal value of a and b.
minmax(a, b, comp) Returns the minimal and maximal value of a and b according to the
predicate comp according to the predicate comp.
minmax(initialiser list) Returns the minimal and maximal value of the initialiser list.
Utilities 12
Function Description
minmax(initialiser list, comp) Returns the minimal and maximal value of the initialiser list
according to the predicate comp.
std::move
The function std::move4 , defined in the header <utility>, empowers the compiler to move it’s
resource. In the so called move semantic, the values from the source object are moved to the new
object. Afterwards the source is in a well-defined but not specified state. Most of the times that is
the default state of the source. By using std::move, the compiler converts the source arg to a rvalue
reference: static_cast<std::remove_reference<decltype(arg)>::type&&> (arg). If the compiler
can not apply the move semantic, it falls back to the copy semantic:
#include <utility>
...
std::vector<int> myBigVec(10000000, 2011);
std::vector<int> myVec;
std::forward
The function std::forward5 , defined in the header <utility>, empower you to write function
templates, which can identically forward their arguments. Typical use cases for std::forward are
factory functions or constructors. Factory functions are functions which create an object and must
therefore identically pass the arguments. Constructors often use their arguments to initialise their
base class with the identical arguments. So std::forward is the perfect tool for authors of generic
libraries:
4 http://en.cppreference.com/w/cpp/utility/move
5 http://en.cppreference.com/w/cpp/utility/forward
Utilities 13
Perfect forwarding
// forward.cpp
...
#include <utility>
...
using std::initialiser_list;
struct MyData{
MyData(int, double, char){};
};
...
int a= createT<int>();
int b= createT<int>(1);
The function template createT has to take their arguments as a universal reference6 : Args&&… args‘.
A universal reference or also called forwarding reference is a rvalue reference in a type deduction
context.
6 https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers
Utilities 14
std::swap
With the function std::swap7 defined in the header <utility>, you can easily swap two objects.
The generic implementation in the C++ standard library internally uses the function std::move.
Move-semantic with std::swap
// swap.cpp
...
#include <utility>
...
template <typename T>
inline void swap(T& a, T& b){
T tmp(std::move(a));
a= std::move(b);
b= std::move(tmp);
}
using std::bind;
using std::function
...
double divMe(double a, double b){ return a/b; };
function < double(double, double) > myDiv1= bind(divMe, _1, _2);
function < double(double) > myDiv2= bind(divMe, 2000, _1);
std::bind
Because of std::bind9 , you can create function objects in a variety of ways:
std::function
std::function10 can store arbitrary callables in variables. It’s a kind of polymorphic function
wrapper. A callable may be a lambda function, a function object or a function. std::function is
always necessary and can’t be replaced by auto, if you have to specify the type of the callable
explicitly.
A dispatch table with std::function
// dispatchTable.cpp
...
#include <functional>
...
using std::make_pair;
using std::map;
The type parameter of std::function defines the type of callables std::function will accept.
int() int
void()
Pairs
With std::pair11 , you can build pairs of arbitrary types. The class template std::pair needs the
header <utility>. std::pair has a default, copy and move constructor. Pair objects can be swapped:
std::swap(pair1, pair2).
Pairs will often be used in the C++ library. For example, the function std::max returns its
result as a pair, the associative container std::map, std::unordered_map, std::multimap and
std::unordered_multimap manage their key/value association in pairs.
To get the elements of a pair p, you can either access it directly or via an index. So, with p.first or
std::get<0>(p) you get the first, with p.second or ‘std::get<1>(p) you get the second element of the
pair.
Pairs support the comparison operators ==, !=, <, >, <= and >=. If you compare two pairs for identity,
at first the members pair1.first and pair2.first will be compared and then pair1.second and
pair2.second. The same strategy holds for the other comparison operators.
std::make_pair
C++ has the practical help function std::make_pair12 to generate pairs, without specifying their
types. std::make_pair automatically deduces their types.
11 http://en.cppreference.com/w/cpp/utility/pair
12 http://en.cppreference.com/w/cpp/utility/pair/make_pair
Utilities 17
// pair.cpp
...
#include <utility>
...
using namespace std;
...
pair<const char*, double> charDoub("str", 3.14);
pair<const char*, double> charDoub2= make_pair("str", 3.14);
auto charDoub3= make_pair("str", 3.14);
cout << charDoub.first << ", " << charDoub.second; // str, 3.14
charDoub.first="Str";
get<1>(charDoub)= 4.14;
cout << charDoub.first << ", " << charDoub.second; // str, 4.14
Tuples
You can create tuples of arbitrary length and types with std::tuple13 . The class template needs the
header <tuple>. std::tuple is a generalization of std::pair. You can convert between tuples with
two elements and pairs. The tuple has, like his small brother std::pair, a default, a copy and a move
constructor. You can swap tuples with the function std::swap.
The i-th element of a tuple t can be referenced by the function template std::get: std::get<i-1>(t).
By std::get<type>(t) you can directly refer to the element of the type type.
Tuples support the comparison operators ==, !=, <, >, <= and >=. If you compare two tuples, the
elements of the tuples will be compared lexicographically. The comparison starts at the index 0.
std::make_tuple
The helper function std::make_tuple14 is quite convenient for the creation of tuples. You don’t have
to provide the types.The compiler automatically deduces them.
13 http://en.cppreference.com/w/cpp/utility/tuple
14 http://en.cppreference.com/w/cpp/utility/tuple/make_tuple
Utilities 18
// tuple.cpp
...
#include <tuple>
...
using std::get;
std::cout << get<0>(tup1) << ", " << get<1>(tup1) << ", "
<< get<2>(tup1) << std::endl; // first, 3, 4.17
std::cout << get<0>(tup2) << ", " << get<1>(tup2) << ", "
<< get<2>(tup2) << std::endl; // second, 4, 1.1
std::cout << (tup1 < tup2) << std::endl; // true
get<0>(tup2)= "Second";
15 http://en.cppreference.com/w/cpp/utility/tuple/tie
16 http://en.cppreference.com/w/cpp/utility/tuple/ignore
Utilities 19
int first= 1;
int second= 2;
int third= 3;
int fourth= 4;
cout << first << " " << second << " "
<< third << " " << fourth << endl; // 1 2 3 4
first= 201;
get<1>(tup)= 202;
cout << get<0>(tup) << " " << get<1>(tup) << " " << get<2>(tup)
<< " " << get<3>(tup) << endl; // 201 202 103 104
cout << first << " " << second << " " << third << " "
<< fourth << endl; // 201 202 103 104
int a, b;
tie(std::ignore, a, std::ignore, b)= tup;
cout << a << " " << b << endl; // 202 104
Reference Wrappers
A reference wrapper is a copy-constructible17 and copy-assignable18 wrapper for a object of type&,
which is defined in the header <functional>. So you have an object, that behaves like a reference,
but can be copied. In opposite to classic references, std::reference_wrapper19 objects support two
additional use cases:
17 http://en.cppreference.com/w/cpp/concept/CopyConstructible
18 http://en.cppreference.com/w/cpp/concept/CopyAssignable
19 http://en.cppreference.com/w/cpp/utility/functional/reference_wrapper
Utilities 20
• You can use them in containers of the Standard Template Library. std::vector<std::reference_-
wrapper<int>> myIntRefVector
• You can copy instances of classes, which have std::reference_wrapper objects. That is in
general not possible with references.
To access the reference of a std::reference_wrapper<int> myInt(1), the get method can be used:
myInt.get(). You can use a reference wrapper to encapsulate and invoke a callable.
Reference wrappers
// referenceWrapperCallable.cpp
...
#include <functional>
...
void foo(){
std::cout << "Invoked" << std::endl;
}
refWrap(); // Invoked
// referenceWrapperRefCref.cpp
...
#include <functional>
...
void invokeMe(const std::string& s){
std::cout << s << ": const " << std::endl;
}
std::string s{"string"};
invokeMe(std::cref(s)); // string
int i= 1;
std::cout << i << std::endl; // 1
doubleMe(i);
std::cout << i << std::endl; // 1S
doubleMe(std::ref(i));
std::cout << i << std::endl; // 2
So it’s possible to invoke the function invokeMe, which gets a constant reference to a std::string,
with a non constant std::string s, which is wrapped in a std::cref(s). If I wrap the variable i in
the helper function std::ref, the function template doubleMe will be invoked with a reference. So
the variable i will be doubled.
Smart Pointers
Smart pointers are one of the most important additions to C++ because they empower you to
implement explicit memory management in C++. Beside the deprecated std::auto_ptr, C++ offers
three different smart pointers. They are defined in the header <memory>.
Firstly there is the std::unique_ptr, which models the concept of exclusive ownership. Secondly,
there is the std::shared_ptr, who models the concept of shared ownership. Lastly, there is the
std::weak_ptr. std::weak_ptr is not so smart, because it has only a thin interface. Its job is it to
break cycles of std::shared_ptr. It models the concept of temporary ownership.
The smart pointers manage their resource according to the RAII idiom. So the resource is automat-
ically released if the smart pointer goes out of scope.
std::unique_ptr
std::unique_ptr22 exclusively takes care of its resource. It automatically releases the resource if it
goes out of scope. If there is no copy semantic required, it can be used in containers and algorithms
of the Standard Template Library. std::unique_ptr is as cheap and fast as a raw pointer, if you use
no special deleter.
#include <memory>
...
std::auto_ptr<int> ap1(new int(2011));
std::auto_ptr<int> ap2 = ap1; // OK
Methods of std::unique_ptr
Name Description
get Returns a pointer to the resource.
get_deleter Returns the delete function.
release Returns a pointer to the resource and releases it.
reset Resets the resource.
swap Swaps the resources.
In the following code sample you can see the application of these methods:
The std::unique_ptr
// uniquePtr.cpp
...
#include <utility>
...
using namepace std;
struct MyInt{
MyInt(int i):i_(i){}
~MyInt(){
cout << "Good bye from " << i_ << endl;
}
int i_;
};
unique_ptr<MyInt> uniquePtr2{move(uniquePtr1)};
cout << uniquePtr1.get() << endl; // 0
cout << uniquePtr2.get() << endl; // 0x15b5010
{
unique_ptr<MyInt> localPtr{new MyInt(2003)};
} // Good bye from 2003
uniquePtr2.reset(new MyInt(2011)); // Good bye from 1998
MyInt* myInt= uniquePtr2.release();
delete myInt; // Good by from 2011
swap(uniquePtr3, uniquePtr4);
cout << uniquePtr3.get() << endl; // 0x15b5010
cout << uniquePtr4.get() << endl; // 0x15b5030
std::unique_ptr array
// uniquePtrArray.cpp
...
#include <memory>
...
using namespace std;
class MyStruct{
public:
MyStruct():val(count){
cout << (void*)this << " Hello: " << val << endl;
MyStruct::count++;
}
~MyStruct(){
cout << (void*)this << " Good Bye: " << val << endl;
MyStruct::count--;
}
private:
int val;
static int count;
};
int MyStruct::count= 0;
...
{
// generates a myUniqueArray with thre `MyStructs`
unique_ptr<MyStruct[]> myUniqueArray{new MyStruct[3]};
}
// 0x1200018 Hello: 0
// 0x120001c Hello: 1
// 0x1200020 Hello: 2
// 0x1200020 GoodBye: 2
// 0x120001c GoodBye: 1
// 0x1200018 GoodBye: 0
Utilities 25
Special Deleters
std::make_unique
The helper function std::make_unique23 was unlike its sibling std::make_shared forgotten in the
C++11 standard. So std::make_unique was added with the C++14 standard. std::make_unique
enables it to create a std::unique_ptr in a single step: std::unique_ptr<int> up= std::make_-
unique<int>(2014).
std::shared_ptr
std::shared_ptr24 shares the ownership of the resource. They have two handles. One for the
resource and one for the reference counter. By copying a std::shared_ptr, the reference count
isincreased by one. It is decreased by one if the std::shared_ptr goes out of scope. If the reference
counter becomes the value 0 and therefore there is no std::shared_ptr referencing the resource, the
C++ runtime automatically releases the resource. The release of the resource takes place at exactly
the time at which the last std::shared_ptr goes out of scope. The C++ runtime guarantees that the
call of the reference counter is an atomic operation. Because of this management, std::shared_ptr
uses more time and memory than a raw pointer or std::unique_ptr.
In the following table are the methods of std::shared_ptr.
Methods of std::shared_ptr
Name Description
get Returns a pointer to the resource.
get_deleter Returns the delete function
reset Resets the resource
swap Swaps the resources.
unique Checks if the std::shared_ptr is the exclusive owner of the resource.
use_count Returns the value of the reference counter.
23 http://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique
24 http://en.cppreference.com/w/cpp/memory/shared_ptr
Utilities 26
std::make_shared
The helper function std::make_shared25 creates the resource and returns it in a std::shared_ptr.
You should use std::make_shared instead of the direct creation of a std::shared_ptr, because
std::make_shared is a lot faster.
// sharedPtr.cpp
...
#include <memory>
...
class MyInt{
public:
MyInt(int v):val(v){
std::cout << "Hello: " << val << std::endl;
}
~MyInt(){
std::cout << "Good Bye: " << val << std::endl;
}
private:
int val;
};
{
std::shared_ptr<MyInt> locSharPtr(sharPtr);
std::cout << locSharPtr.use_count() << std::endl; // 2
}
std::cout << sharPtr.use_count() << std::endl; // 1
globSharPtr.reset();
std::cout << sharPtr.use_count() << std::endl; // 1
sharPtr= std::shared_ptr<MyInt>(new MyInt(2011)); // Hello:2011
// Good Bye: 1998
...
// Good Bye: 2011
25 http://en.cppreference.com/w/cpp/memory/shared_ptr/make_shared
Utilities 27
The callable is in this example a function object. Therefore you can easily count how many instances
of a class were created. The result is in the static variable count.
You can create with the class std::enable_shared_from_this26 objects which return a std::shared_-
ptr on itself. For that you have to derive the class public from std::enable_shared_from_this. So
the class support the method shared_from_this to return std::shared_ptr to this:
std::shared_ptr from this
// enableShared.cpp
...
#include <memory>
...
class ShareMe: public std::enable_shared_from_this<ShareMe>{
std::shared_ptr<ShareMe> getShared(){
return shared_from_this();
}
};
You can see in the code sample that the get methods reference the same object.
std::weak_ptr
To be honest, std::weak_ptr27 is not a smart pointer. std::weak_ptr supports no transparent access
to the resource because it only borrows the resource from a std::shared_ptr. std::weak_ptr does
not change the reference counter:
26 http://en.cppreference.com/w/cpp/memory/enable_shared_from_this
27 http://en.cppreference.com/w/cpp/memory/weak_ptr
Utilities 28
std::weak_ptr
// weakPtr.cpp
...
#include <memory>
...
auto sharedPtr= std::make_shared<int>(2011);
std::weak_ptr<int> weakPtr(sharedPtr);
weakPtr.reset();
Methods of std::weak_ptr
Name Description
expired Checks if the resource was deleted.
lock Creates a std::shared_ptr on the resource.
reset Resets the resource
swap Swaps the resources.
use_count Returns the value of the reference counter.
There is one reason for the existence of std::weak_ptr. It breaks the cycle of std::shared_ptr.
Utilities 29
Cyclic References
You get cyclic references of std::shared_ptr if they refer to each other. So, the resource counter
never becomes 0 and the resource is not automatically released. You can break this cycle if you
embed a std::weak_ptr in the cycle. std::weak_ptr does not modify the reference counter.
The result of the code sample is that the daughter automatically released, but not the son nor the
mother. The mother refers her son via a std::shared_ptr, her daughter via a std::weak_ptr. Maybe
it helps to see the structure of the code in an image.
Cyclic references
Cyclic references
// cyclicReference.cpp
...
#include <memory>
...
using namespace std;
struct Mother{
~Mother(){cout << "Mother gone" << endl;}
void setSon(const shared_ptr<Son> s ){mySon= s;}
void setDaughter(const shared_ptr<Daughter> d){myDaughter= d;}
shared_ptr<const Son> mySon;
weak_ptr<const Daughter> myDaughter;
};
struct Son{
Son(shared_ptr<Mother> m):myMother(m){}
~Son(){cout << "Son gone" << endl;}
shared_ptr<const Mother> myMother;
};
struct Daughter{
Daughter(shared_ptr<Mother> m):myMother(m){}
~Daughter(){cout << "Daughter gone" << endl;}
shared_ptr<const Mother> myMother;
};
{
shared_ptr<Mother> mother= shared_ptr<Mother>(new Mother);
shared_ptr<Son> son= shared_ptr<Son>(new Son(mother) );
shared_ptr<Daughter> daugh= shared_ptr<Daughter>(new Daughter(mother));
mother->setSon(son);
mother->setDaughter(daugh);
}
// Daughter gone
Utilities 31
Type Traits
The type traits library28 enables you, to check, to compare and to modify types at compile time. So,
there is no overhead on the runtime of your program. There are two reasons for using the type traits
library: Optimization and Correctness. Optimization, because the introspection capabilities of the
type traits library make it possible to choose the faster code automatically. Correctness, because you
can specify requirements for your code, which is checked at compile time.
#include <type_traits>
...
template <typename T>T fac(T a){
static_assert(std::is_integral<T>::value, "T not integral");
...
}
fac(10);
fac(10.1); // with T= double; T not integral
The GCC compiler quits the function invocation fac(10.1). The message at compile is that
T is of type double and therefore no integral type.
There are 14 different type categories. They are complete and don’t overlap. So each type is only a
member of one type category. If you check a type category for your type, the request is independent
of the const or volatile qualifiers.
28 http://en.cppreference.com/w/cpp/header/type_traits
Utilities 32
// typeCategories.cpp
...
#include <type_traits>
using std::cout;
struct A{
int a;
int f(int){ return 2011; }
};
cout << std::is_member_object_pointer<int A::*>::value; // true
cout << std::is_member_function_pointer<int (A::*)(int)>::value; // true
enum E{
e= 1,
};
cout << std::is_enum<E>::value; // true
union U{
Utilities 33
int u;
};
cout << std::is_union<U>::value; // true
Based on the 14 primary type categories, there are 6 composite type categories.
Type Properties
In addition to the primary and composite type categories there are type properties.
Type Comparisons
The library supports three kinds of type comparisons.
Utilities 35
Type comparison
Function Description
template class Base, class Derived> Checks if Derived is derived from Base.
struct is_base_of
template <class From, class To> Checks if From can be converted to To.
struct is_convertible
template <class T, class U> Checks if the types T and U are the same.
struct is_same
Type modifications
The type traits library enables you to modify types during compile time. So you can modify the
constness of a type:
Type modifications
// typeTraitsModifications.cpp
...
#include <type_traits>
...
using namespace std;
The function std::add_const adds the constness to a type, while std::remove_const removes it.
There are a lot more functions available in the type traits library. So you can modify the const-volatile
properties of a type.
Utilities 36
The three following functions are especially valuable for the writing of generic libraries.
You can conditionally hide with std::enable_if a function overload or template specialization from
overload resolution. std::conditional provides you with the ternary operator at compile time and
std::common_type gives you the type, to which all type parameter can be implicitly converted to.
std::common_type is a variadic template29 , therefore the number of type parameters can be arbitrary.
29 http://en.cppreference.com/w/cpp/language/parameter_pack
Utilities 37
Time Library
The time library30 is a key component of the new multithreading capabilities of C++. So you
can put the current thread by std::this_thread::sleep_for(std::chrono::milliseconds(15) for
15 milliseconds to sleep, or you try to acquire a lock for 2 minutes: lock.try_lock_until(now
+ std::chrono::minutes(2)). Beside that, the chrono library makes it easy to perform simple
performance tests:
Performance measurement
// performanceMeasurement.cpp
...
#include <chrono>
...
std::vector<int> myBigVec(10000000, 2011);
std::vector<int> myEmptyVec1;
The time library consists of the three components, time point, time duration and clock.
Time point
Time point is defined by a starting point, the so-called epoch, and an additional time duration.
Time duration
Time duration is the difference between two time-points. It is given by the number of ticks.
Clock
A clock consists of a starting point (epoch) and a tick, so that the current time point can be
calculated.
Time Point
A duration consists of a span of time, defined as some number of ticks of some time unit. A time
point consists of a clock and a time duration. This time duration can be positive or negative.
30 http://en.cppreference.com/w/cpp/header/chrono
Utilities 38
// epoch.cpp
...
#include <chrono>
...
auto timeNow= std::chrono::system_clock::now();
auto duration= timeNow.time_since_epoch();
std::cout << duration.count() << "ns" // 1413019260846652ns
Time Duration
Time duration is the difference between two time-points. Time duration is measured in the number
of ticks.
If Rep is a floating point number, the time duration supports fractions of ticks. The most important
time durations are predefined in the chrono library:
Utilities 39
How long can a time duration be? The C++ standard guarantees that the predefined time durations
can store +/- 292 years. You can easily define your own time duration like a German school hour:
typedef std::chrono::duration<double, std::ratio<2700>> MyLessonTick. Time durations in
natural numbers have to be explicitly converted to time durations in floating pointer numbers. The
value will be truncated:
Durations
// duration.cpp
...
#include <chrono>
#include <ratio>
using std::chrono;
milliseconds milli(aSecond);
std::cout << milli.count() << " milli"; // 1000 milli
seconds seconds(aSecond);
std::cout << seconds.count() << " sec"; // 1 sec
minutes minutes(duration_cast<minutes>(aSecond));
std::cout << minutes.count() << " min"; // 0 min
std::ratio
std::ratio supports arithmetic at compile time with rational numbers. A rational number has
two template arguments. One is the nominator, the other the denominator. C++11 predefines
lots of rational numbers.
C++14 has built-in literals for the most used time durations.
Clock
The clock consists of a starting point and a tick. So you can get the current time with the method
now.
Utilities 41
std::chrono::system_clock
System time, which you can synchronise with the external clock.
std::chrono::steady_clock
Clock, which can not be adjusted.
std::chrono::high_resolution_clock
System time with the greatest accuracy.
std::chrono::system_clock will refer typically to the 1.1.1970. You can not adjust std::steady_-
clock forward or backward in opposite to two other clocks. The methods to_time_t and from_time_t
can be used to convert between std::chrono::system_clock and std::time_t objects.
std::any
std::any32 is a type-safe container for single values of any type which is copy-constructible. There
are a few ways to create a std::any container any. You can use the various constructors or the
factory function std::make_any. By using any.emplace, you directly construct one value into any.
any.reset lets you destroy the contained object. If you want to know whether the container any
has a value, use the method any.has_value. You can even get the typeid of the container object via
any.type. Thanks to the generic function std::any_cast you have access to the contained object. If
you specify the wrong type, you will get a std::bad_any_cast exception.
Here is a code snippet showing the basic usage of std::any.
std::any
// any.cpp
...
#include <any>
struct MyClass{};
...
The program snippet defines a std::vector<std::any>. To get one of its elements, you have to
use std::any_cast. As mentioned, if you use the wrong type, you will get a std::bad_any_cast
exception.
std::any can have objects of arbitrary types; std::optional may or may not have a value.
std::optional
std::optional33 is quite comfortable for calculations such as database queries that may have a
result.
The variuous constructors and the convenience function std::make_optional let you define an
optional object opt with or without a value. opt.emplace will construct the contained value in-place
and opt.reset will destroy the container value. You can explicitly ask a std::optional container if it
has a value or you can check it in a logical expression. opt.value returns the value and opt.value_or
returns the value or a default value. If opt has no contained value, the call opt.value will throw a
std::bad_optional_access exception.
33 http://en.cppreference.com/w/cpp/utility/optional
Utilities 43
std::optional
// optional.cpp
...
#include <optional>
...
if (myInt){
std::cout << *myInt << std::endl; // 1
std::cout << myInt.value() << std::endl; // 1
std::cout << myInt.value_or(2017) << std::endl; // 1
}
if (!myEmptyInt){
std::cout << myEmptyInt.value_or(2017) << std::endl; // 2017
}
I use std::optional in the function getFirst. getFirst returns the first element if it exists. If
not, you will get a std::optional<int> object. The main function has two vectors. Both invoke
getFirst and return a std::optional object. In the case of myInt the object has a value; in the case
of myEmptyInt, the object has no value. The program displays the value of myInt and myEmptyInt.
myInt.value_or(2017) returns the value, but myEmptyInt.value_or(2017) returns the default value.
std::variant, explained in the next section, can have more than one value.
std::variant
std::variant34 is a type-safe union. An instance of std::variant has a value from one of its types.
The type must not be a reference, array or void. A std::variant can have a type more than once.
34 http://en.cppreference.com/w/cpp/utility/variant
Utilities 44
A default-initialised std::variant is initialised with its first type; therefore, its first type must
have a default constructor. By using var.index you get the zero-based index of the alternative
held by the std::variant var. var.valueless_by_exception returns false if the variant holds a
value. By using var.emplace you can create a new value in-place. There are a few global functions
used to access a std:variant. The function template var.holds_alternative lets you check if the
std::variant holds a specified alternative. You can use std::get with an index and with a type as
argument. By using an index, you will get the value. If you invoke std::get with a type, you only
will get the value if it is unique. If you use an invalid index or a non-unique type, you will get a
std::bad_variant_access exception. In contrast to std::get which eventually returns an exception,
std::get_if returns a null pointer in the case of an error.
// variant.cpp
...
#include <variant>
...
std::variant<int, float> v, w;
v = 12; // v contains int
int i = std::get<int>(v);
w = std::get<int>(v);
w = std::get<0>(v); // same effect as the previous line
w = v; // same effect as the previous line
try{
std::get<float>(w); // w contains int, not float: will throw
}
catch (std::bad_variant_access&) {}
v and w are two variants. Both can have an int and a float value. Their default value is 0. v
becomes 12 and the following call std::get<int>(v) returns the value. The next three lines show
three possibilities to assign the variant v to w, but you have to keep a few rules in mind. You
can ask for the value of a variant by type std::get<double>(v) or by index: std::get<3>(v).
The type must be unique and the index valid. The variant w holds an int value; therefore, I get
Utilities 45
a std::bad_variant_access exception if I ask for a float type. If the constructor call or assignment
call is unambiguous, a conversion can take place. This is the reason that it’s possible to construct a
std::variant<std::string> from a C-string or assign a new C-string to the variant.
std::variant has a interesting non-member function std::visit that allows you the execute a
callable on a list of variants. A callable is something which you can invoke. Typically this can be a
function, a function object, or lambda expression. For simplicity reasons, I use a lambda function in
this example.
std::visit
// visit.cpp
...
#include <variant>
...
Each variant in this example can hold a char, long, float, int, double, or long long. The first
visitor [](auto&& arg){std::cout << arg << " ";} will output the various variants. The second
visitor std::cout << typeid(arg).name() << " ";} will display its types.
Now I want to sum up the elements of the variants. First I need the right result type at compile time.
std::common_type from the type traits library will provide it. std::common_type gives the type to
which all types char, long, float, int, double, and long long can implicitly be converted to.
The final {} in res{} causes it to be initialised to 0.0. res is of type double. The visitor [&res](auto&&
arg){arg *= 2;} calculates the sum and the following line displays it.
3. Interface of All Containers
Although the sequential and associative containers of the Standard Template library are two quite
different classes of containers, they have a lot in common. For example, the operations, to create or
delete a container, to determine its size, to access its elements, to assign or swap are all independent
of the type of elements of a container. It is common for the containers that you can define them with
an arbitrary size, and each container has an allocator. That’s the reason the size of a container can be
adjusted at runtime. The allocator works most of the time in the background. This can be seen for a
std::vector. The call std::vector<int> results in a call std::vector<int, std::allocator<int>>.
Because of the std::allocator, you can adjust except for std::array the size of all containers
dynamically. However, they have yet more in common. You can access the elements of a container
quite easily with an iterator.
Having so much in common, the containers differ in the details. The chaptersSequential Container
and Associative Container provide the details.
With the sequential containers std::array, std::vector, std::deque, std::list, and std::forward_-
list C++ has an expert on each domain.
The same holds true for the associative containers, which can be classified in the order and unordered
ones.
Type Example
Default std::vector<int> vec1
Type Example
Destructor vec5.∼vector()
Because std::array is generated at compile time, there are a few things that are special. std::array
has no move constructor and can neither be created with a range nor with an initialiser list. However,
you can initialize a std::array with an aggregate initialisation. Also, std::array has no method
for removing its elements.
Now I can use the different constructors on the different containers.
Various constructors
// containerConstructor.cpp
...
#include <map>
#include <unordered_map>
#include <vector>
...
using namespace std;
vec3.clear();
cout << vec3.size() << endl; // 0
Size
For a container cont, you can check with cont.empty() if the container is empty. cont.size()
returns the current number of elements, and cont.max_size() returns the maximum number of
elements cont can have. The maximum number of elements is implementation defined.
Size of a container
// containerSize.cpp
...
#include <map>
#include <set>
#include <vector>
...
using namespace std;
Access
To access the elements of a container, you can use an iterator. If you use a begin and end iterator, you
have a range, which you can further process. For a container cont, you get with cont.begin() the
begin iterator and with cont.end() the end iterator, which defines a half-open range. It is half-open
because the begin iterator belongs to the range, the end iterator refers to a position past the range.
With the iterator pair cont.begin() and cont.end() you can modify the elements.
Iterator Description
cont.begin() and cont.end() Pair of iterators to iterate forward.
cont.cbegin() and cont.cend() Pair of iterators to iterate const forward.
cont.rbegin() and cont.rend() Pair of iterators to iterate backward.
cont.crbegin() and cont.crend() Pair of iterators to iterate const backward.
// containerAccess.cpp
...
#include <vector>
...
struct MyInt{
MyInt(int i): myInt(i){};
int myInt;
};
std::vector<MyInt> myIntVec;
myIntVec.push_back(MyInt(5));
myIntVec.emplace_back(1);
std::cout << myIntVec.size() << std::endl; // 2
std::vector<int> intVec;
intVec.assign({1, 2, 3});
for (auto v: intVec) std::cout << v << " "; // 1 2 3
intVec.insert(intVec.begin(), 0);
for (auto v: intVec) std::cout << v << " "; // 0 1 2 3
intVec.insert(intVec.begin()+4, 4);
Interface of All Containers 51
intVec.pop_back();
for (auto v: intVec ) std::cout << v << " "; // 0 1 2 3 4 5 6 7 8 9 10
// containerAssignmentAndSwap.cpp
...
#include <set>
...
std::set<int> set1{0, 1, 2, 3, 4, 5};
std::set<int> set2{6, 7, 8, 9};
set1= set2;
for (auto s: set1) std::cout << s << " "; // 6 7 8 9
for (auto s: set2) std::cout << s << " "; // 6 7 8 9
set1= std::move(set2);
for (auto s: set1) std::cout << s << " "; // 6 7 8 9
for (auto s: set2) std::cout << s << " "; //
std::swap(set1, set2);
for (auto s: set1) std::cout << s << " "; // 60 70 80 90
for (auto s: set2) std::cout << s << " "; // 6 7 8 9
Compare
Containers support the comparison operators ==, !=, <, >, <=, >=. The comparison of two containers
happens on the elements of the containers. If you compare associative containers, their key is
compared. Unordered associative containers support only the comparison operator == and !=.
Comparison of a container
// containerComparison.cpp
...
#include <array>
#include <set>
#include <unordered_map>
#include <vector>
...
using namespace std;
k-times slower.
Although the random access on the elements of a std::vector has the same complexity O(1) as the
random access on the element of a std::deque, that doesn’t mean, that both operations are equally
fast.
The complexity guarantee O(1) for the insertion or deletion into a double (std::list) or single
linked list (std::forward_list) is only guaranteed if the iterator points to the right element.
Arrays
std::array2 is a homogeneous container of fixed length. It needs the header <array>. The
std::array combines the memory and runtime characteristic of a C array with the interface of
std::vector. This means in particular, the std::array knows its size. You can use std::array in
the algorithms of the STL.
You have to keep a few special rules in your mind to initialise a std::array.
2 http://en.cppreference.com/w/cpp/container/array
Sequential Containers 56
arr[n];
arr.at(n);
std::get<n>(arr);
The most often used first type form with angle brackets does not check the boundaries of the arr.
This is in opposition to arr.at(n). You will get eventually a std::range-error exception. The last
type shows the relationship of the std::array with the std::tuple, because both are containers of
fixed length.
Here is a little bit of arithmetic with std::array.
std::array
// array.cpp
...
#include <array>
...
std::array<int, 10> arr{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (auto a: arr) std::cout << a << " " ; // 1 2 3 4 5 6 7 8 9 10
Vectors
std::vector3 is a homogeneous container, for which it’s length can be adjusted at runtime.
std::vector needs the header <vector>. As it stores its elements contiguously in memory, std::vector
support pointer arithmetic.
3 http://en.cppreference.com/w/cpp/container/vector
Sequential Containers 57
std::vector<int> vec(10);
std::vector<int> vec{10};
Method Description
vec.size() Number of elements of vec.
vec.capacity() Number of elements, which vec can have without
reallocation.
vec.resize(n) vec will be increased to n elements.‘
The call vec.shrink_to_fit() is not binding. That means the runtime can ignore it. But on the
popular platforms, I always observed the desired behaviour.
So let’s see the methods in the application.
Sequential Containers 58
std::vector
// vector.cpp
...
#include <vector>
...
std::vector<int> intVec1(5, 2011);
intVec1.reserve(10);
std::cout << intVec1.size() << std::endl; // 5
std::cout << intVec1.capacity() << std::endl; // 10
intVec1.shrink_to_fit();
std::cout << intVec1.capacity() << std::endl; // 5
std::vector<int> intVec2(10);
std::cout << intVec2.size() << std::endl; // 10
std::vector<int> intVec3{10};
std::cout << intVec3.size() << std::endl; // 1
std::vector vec has a few methods to access its elements. With vec.front() you get the first
element, with vec.back() you get the last element of vec. To read or write the (n+1)-th element of
vec, you can use the index operator vec[n] or the method vec.at(n). The second one checks the
boundaries of vec, so that you eventually get a std::range_error exception.
Besides the index operator, std::vector offers additional methods to assign, insert, create or remove
elements. See the following overview.
Method Description
vec.assign( ... ) Assigns one or more elements, a range or an initialiser list.
vec.clear() Removes all elements from vec.
vec.emplace(pos, args ... ) Creates a new element before poswith the args in vec and returns the
new position of the element.
vec.emplace_back(args ... ) Creates a new element in vec with args ... .
vec.erase( ... ) Removes one element or a range and returns the next position.
vec.insert(pos, ... ) Inserts one or more elements, a range or an initialiser list and returns the
new position of the element.
Sequential Containers 59
Method Description
Deques
std::deque
// deque.cpp
...
#include <deque>
...
struct MyInt{
MyInt(int i): myInt(i){};
int myInt;
};
std::deque<MyInt> myIntDeq;
myIntDeq.push_back(MyInt(5));
myIntDeq.emplace_back(1);
std::cout << myIntDeq.size() << std::endl; // 2
std::deque<MyInt> intDeq;
intDeq.assign({1, 2, 3});
for (auto v: intDeq) std::cout << v << " "; // 1 2 3
intDeq.insert(intDeq.begin(), 0);
for (auto v: intDeq) std::cout << v << " "; // 0 1 2 3
4 http://en.cppreference.com/w/cpp/container/deque
Sequential Containers 60
intDeq.insert(intDeq.begin()+4, 4);
for (auto v: intDeq) std::cout << v << " "; // 0 1 2 3 4
intDeq.pop_back();
for (auto v: intDeq) std::cout << v << " "; // 0 1 2 3 4 5 6 7 8 9 10
intDeq.push_front(-1);
for (auto v: intDeq) std::cout << v << " "; // -1 0 1 2 3 4 5 6 7 8 9 10
Lists
5 http://en.cppreference.com/w/cpp/container/list
Sequential Containers 61
Method Description
lis.merge(c) Merges the sorted list c into the sorted list lis, so that lis keeps sorted.
lis.merge(c, op) Merges the sorted list c into the sorted list lis, so that lis keeps sorted. Uses op
as sorting criteria.
lis.remove(val) Removes all elements from lis with value val.
lis.remove_if(pre) Removes all elements from lis, fulfilling the predicate pre.
lis.splice(pos, ... ) Splits the elements in lis before pos. The elements can be single elements,
ranges or lists.
lis.unique() Removes adjacent element with the same value.
lis.unique(pre) Removes adjacent elements, fulfilling the predicate pre.
// list.cpp
...
#include <list>
...
std::list<int> list1{15, 2, 18, 19, 4, 15, 1, 3, 18, 5,
4, 7, 17, 9, 16, 8, 6, 6, 17, 1, 2};
list1.sort();
for (auto l: list1) std::cout << l << " ";
// 1 1 2 2 3 4 4 5 6 6 7 8 9 15 15 16 17 17 18 18 19
list1.unique();
for (auto l: list1) std::cout << l << " ";
// 1 2 3 4 5 6 7 8 9 15 16 17 18 19
Forward Lists
std::forward_list6 is a singly linked list, which needs the header <forward_list>. std::forward_-
list has a drastically reduced interface and is optimised for minimal memory requirements.
The characteristic that you can iterator a std::forward_list forward has a great impact. So the
iterators cannot be decremented and therefore, operations like It-- on iterators are not supported.
For the same reason, std::forward_list has no backward iterator. std::forward_list is the only
sequential container which doesn’t know it size.
Method Description
forw.before_begin() Returns an iterator before the first element.
forw.emplace_after(pos, args... ) Creates an element after pos with the arguments args....
forw.emplace_front(args... ) Creates an element at the beginning of forw with the arguments
args....
forw.erase_after( pos, ... ) Removes from forw the element pos or a range of elements,
starting with pos.
6 http://en.cppreference.com/w/cpp/container/forward_list
Sequential Containers 63
Method Description
forw.insert_after(pos, ... ) Inserts after pos new elements. These elements can be single
elements, ranges or initialiser lists.
forw.merge(c) Merges the sorted forward list c into the sorted forward list forw,
so that forw keeps sorted.
forw.merge(c, op) Merges the forward sorted list c into the forward sorted list forw,
so that forw keeps sorted. Uses os as sorting criteria.
forw.splice_after(pos, ... ) Splits the elements in forw before pos. The elements can be single
elements, ranges or lists.
forw.unique() Removes adjacent element with the same value.
forw.unique(pre) Removes adjacent elements, fulfilling the predicate pre.
// forwardList.cpp
...
#include<forward_list>
...
using std::cout;
std::forward_list<int> forw;
std::cout << forw.empty() << std::endl; // true
forw.push_front(7);
forw.push_front(6);
forw.push_front(5);
forw.push_front(4);
forw.push_front(3);
forw.push_front(2);
forw.push_front(1);
for (auto i: forw) cout << i << " "; // 1 2 3 4 5 6 7
forw.erase_after(forw.before_begin());
cout<< forw.front(); // 2
std::forward_list<int> forw2;
forw2.insert_after(forw2.before_begin(), 1);
forw2.insert_after(forw2.before_begin()++, 2);
Sequential Containers 64
forw2.insert_after((forw2.before_begin()++)++, 3);
forw2.push_front(1000);
for (auto i= forw2.cbegin();i != forw2.cend(); ++i) cout << *i << " ";
// 1000 3 2 1
forw.sort();
for (auto i= forw.cbegin(); i != forw.cend(); ++i) cout << *i << " ";
// 1 2 2 3 3 4 5 6 7 1000
forw.reverse();
for (auto i= forw.cbegin(); i != forw.cend(); ++i) cout << *i << " ";
// 1000 7 6 5 4 3 3 2 2 1
forw.unique();
for (auto i= forw.cbegin(); i != forw.cend(); ++i) cout << *i << " ";
// 1000 7 6 5 4 3 2 1
5. Associative Container
C++ has eight different associative containers1 . Four of them are associative containers with sorted
keys: std::set, std::map, std::multiset and std::multimap. The other four are associative con-
tainers with unsorted keys: std::unordered_set, std::unordered_map, std::unordered_multiset
and std::unordered_multimap. The associative containers are special containers. That means they
support all of the operations described in the chapter Interface of all containers.
Overview
All eight ordered and unordered containers have in common that they associate a key with a value.
You can use the key to get the value. To get a classification of the associative containers, three simple
questions need to be answered:
The following table with 2^3= 8 rows gives the answers to the three questions. I answer a fourth
question in the table. How fast is the access time of a key in the best case?
Associative container Sorted Associated value More identical keys Access time
std::set yes no no logarithmic
std::unordered_set no no no constant
std::map yes yes no logarithmic
std::unordered_map no yes no constant
std::multiset yes no yes logarithmic
std::unordered_multiset no no yes constant
std::multimap yes yes yes logarithmic
std::unordered_multimap no yes yes constant
Since C++98, C++ has ordered associative containers; with C++11, C++ has in addition unordered
1 http://en.cppreference.com/w/cpp/container
Associative Container 66
associative containers. Both classes have a very similar interface. That’s the reason that the following
code sample is identical for std::map and std::unordered_map. To be more precise, the interface of
std::unordered_map is a superset of the interface of std::map. The same holds for the remaining
three unordered associative containers. So the porting of your code from the ordered to unordered
containers is quite easy.
You can initialise the containers with an initialiser list and add new elements with the index operator.
To access the first element of the key/value pair p, you have p.first, and for the second element,
you have p.second. p.first is the key and p.second is the associated value of the pair.
std::map versus std::unordered_map
// orderedUnorderedComparison.cpp
...
#include <map>
#include <unordered_map>
// std::map
// std::unordered_map
There is a subtle difference between the two program executions: The keys of the std::map are
ordered, the keys of the std::unordered_map are unordered. The question is: Why do we have
Associative Container 67
such similar containers in C++? I already pointed it out in the table. The reason is so often the
same: performance. The access time to the keys of an associative container is constant and therefore
independent of the size of the container. If the containers are big enough, the performance difference
is significant. Have a look at the section about the performance.
mySet.insert(8);
std::array<int, 5> myArr{10, 11, 12, 13, 14};
mySet.insert(myArr.begin(), myArr.begin()+3);
mySet.insert({22, 21, 20});
for (auto s: mySet) std::cout << s << " ";
// 1 1 2 2 3 3 3 3 4 4 4 4 5 5 6 6 7 10 11 12 20 21 22
Overview
The ordered associative container std::map and std::multimap associate their key with a value.
Both are defined in the header <map>. std::set and std::multiset need the header <set>. This
Table gives you the details.
Associative Container 68
All four ordered containers are parametrised by their type, their allocator and their comparison
function. The containers have default values for the allocator and the comparison function,
depending on the type. The declaration of std::map and std::set shows this very nicely.
The declaration of both associative containers shows that std::map has an associated value. The key
and the value are used for the default allocator: allocator<pair<const key, val>>. With a little bit
more imagination, you can derive more from the allocator. std::map has pairs of the type std::pair<
const key, val>. The associated value val does not matter for the sort criteria: less<key>. All
observations also hold for std::multimap and std::multiset.
• default constructible,
• copyable and moveable.
The with the key associated value builds a pair p so that you get with the member p.first the value
p.second.
#include <map>
...
std::multimap<char, int> multiMap= {{'a', 10}, {'a', 20}, {'b', 30}};
for (auto p: multiMap) std::cout << "{" << p.first << "," << p.second << "} ";
// {a,10} {a,20} {b,30}
Associative Container 69
You can specify the sorting criterion as a template argument. This sorting criterion must implement
a strict weak ordering.
In opposite to the definition of the strict weak ordering, the usage of a comparison criterion with
strict weak ordering is a lot simpler for a std::map.
#include <map>
...
std::map<int, std::string, std::greater<int>> int2Str{
{5, "five"}, {1, "one"}, {4, "four"}, {3, "three"},
{2, "two"}, {7, "seven"}, {6, "six"} };
for (auto p: int2Str) std::cout << "{" << p.first << "," << p.second << "} ";
// {7,seven} {6,six} {5,five} {4,four} {3,three} {2,two} {1,one}
// associativeContainerSearch.cpp
...
#include <set>
...
std::multiset<int> mySet{3, 1, 5, 3, 4, 5, 1, 4, 4, 3, 2, 2, 7, 6, 4, 3, 6};
mySet.erase(mySet.lower_bound(4), mySet.upper_bound(4));
for (auto s: mySet) std::cout << s << " ";
// 1 1 2 2 3 3 3 3 5 5 6 6 7
std::cout << mySet.count(3) << std::endl; // 4
std::cout << *mySet.find(3) << std::endl; // 3
std::cout << *mySet.lower_bound(3) << std::endl; // 3
std::cout << *mySet.upper_bound(3) << std::endl; // 5
auto pair= mySet.equal_range(3);
std::cout << "(" << *pair.first << "," << *pair.second << ")"; // (3,5)
Associative Container 71
std::map
std::map2 is the by far the most frequently used associative container. The reason is simple. It
combines a often sufficient enough performance with a very convenient interface. You can access
its elements via the index operator. If the key doesn’t exist, std:map creates a key/value pair. For the
value, the default constructor is used.
In addition to the index operator, std::map supports the at method. The access via at is checked. So
if the request key doesn’t exist in the std::map, a std::out_of_range exception is thrown.
2 http://en.cppreference.com/w/cpp/container/map
Associative Container 72
Overview
With the new C++11 standard, C++ has four unordered associative containers: std::unordered_-
map, std::unordered_multimap, std::unordered_set and std::unordered_multiset. They have a
lot in common with their namesakes, the ordered associative containers. The difference is that the
unordered ones have a richer interface and their keys are not sorted.
This shows the declaration of a std::unordered_map.
value of its key: std::has<key> and second, to compare the keys for equality: std::equal_to<key>.
Because of the three default template parameters , you have only to provide the type of the key and
the value of the std::unordered_map: std::unordered_map<char,int> unordMap.
• equal comparable,
• available as hash value,
• copyable or moveable.
• default constructible,
• copyable or moveable.
Performance
Performance - that’s the simple reason - why the unordered associative containers were so long
missed in C++. In the example below, one million randomly created values are read from a 10 million
big std::map and std::unordered_map. The impressive result is that the linear access time of an
unordered associative container is 20 times faster than the access time of an ordered associative
container. That is just the difference between constant and logarithmic complexity O(log n) of these
operations.
Performancecomparison
// associativeContainerPerformance.cpp
...
#include <map>
#include <unordered_map>
...
using std::chrono::duration;
static const long long mapSize= 10000000;
static const long long accSize= 1000000;
...
// read 1 million arbitrary values from a std::map
// with 10 million values from randValues
auto start= std::chrono::system_clock::now();
for (long long i= 0; i < accSize; ++i){myMap[randValues[i]];}
Associative Container 74
• is already defined for the built-in types like boolean, natural numbers and floating point
numbers,
• is available for std::string and std::wstring,
• generates for a C string const char a hash value of the pointer address,
• can be defined for user-defined data types.
For user-defined types which are used as a key for an unordered associative container, you have to
keep two requirements to keep in mind. They need a hash function and have to be comparable to
equal.
A custom hash function
// unorderedMapHash.cpp
...
#include <unordered_map>
...
struct MyInt{
MyInt(int v):val(v){}
bool operator== (const MyInt& other) const {
return val == other.val;
}
Associative Container 75
int val;
};
struct MyHash{
std::size_t operator()(MyInt m) const {
std::hash<int> hashVal;
return hashVal(m.val);
}
};
for(auto m : myMap) std::cout << "{" << m.first << "," << m.second << "} ";
// {MyInt(1),1} {MyInt(0),0} {MyInt(-1),-1} {MyInt(-2),-2}
The Details
The unordered associative containers store their indices in buckets. In which bucket the index goes
is due to the hash function, which maps the key to the index. If different keys are mapped to the
same index, it’s called a collision. The hash function tries to avoid this.
Indices are typically be stored in the bucket as a linked list. Since the access to the bucket is constant,
the access in the bucket is linear. The number of buckets is called capacity, the average number of
elements for each bucket is called the load factor. In general, the C++ runtime generates new buckets
if the load factor is greater than 1. This process is called rehashing and can also explicitly be triggered:
Associative Container 76
// hashInfo.cpp
...
#include <unordered_set>
...
using namespace std;
unordered_set<int> hash;
cout << hash.max_load_factor() << endl; // 1
getInfo(hash);
// hash.bucket_count(): 1
// hash.load_factor(): 0
hash.insert(500);
cout << hash.bucket(500) << endl; // 5
getInfo(hash);
// hash.bucket_count(): 109
// hash.load_factor(): 0.88908
hash.rehash(500);
getInfo(hash);
// hash.bucket_count(): 541
// hash.load_factor(): 0.17298
cout << hash.bucket(500); // 500
With the method max_load_factor, you can read and set the load factor. So you can influence the
probability of collisions and rehashing. I want to emphasise one point in the short example above.
The key 500 is at first in the 5th bucket, but after rehashing is in the 500th bucket.
6. Adaptors for Containers
C++ has with std::stack, std::queue and std::priority_queue three special sequential containers.
I guess, most of you know these classic data structures from your education.
The adaptors for containers
Stack
The std::stack1 follows the LIFO principle (Last In First Out). The stack sta, which needs the
header <stack>, has three special methods.
With sta.push(e) you can insert a new element e at the top of the stack, remove it from the top
with sta.pop() and reference it with sta.top(). The stack supports the comparison operators and
knows its size. The operations of the stack have constant complexity.
1 http://en.cppreference.com/w/cpp/container/stack
Adaptors for Containers 78
std::stack
// stack.cpp
...
#include <stack>
...
std::stack<int> myStack;
myStack.push(1);
myStack.push(2);
myStack.push(3);
std::cout << myStack.top() << std::endl; // 3
while (!myStack.empty()){
std::cout << myStack.top() << " ";
myStack.pop();
} // 3 2 1
Queue
The std::queue2 follows the FIFO principle (First In First Out). The queue que, which needs the
header <queue>, has four special methods.
With que.push(e) you can insert an element e at the end of the queue and remove the first element
from the queue with que.pop(). que.back() enables you to refer to the last element in the que,
2 http://en.cppreference.com/w/cpp/container/queue
Adaptors for Containers 79
que.front() to the first element in the que. std::queue has similar characteristics as std::stack.
So you can compare std::queue instances and get their sizes. The operations of the queue have
constant complexity.
std::queue
// queue.cpp
...
#include <queue>
...
std::queue<int> myQueue;
myQueue.push(1);
myQueue.push(2);
myQueue.push(3);
std::cout << myQueue.back() << std::endl; // 3
std::cout << myQueue.front() << std::endl; // 1
while (!myQueue.empty()){
std::cout << myQueue.back() << " ";
std::cout << myQueue.front() << " : ";
myQueue.pop();
} // 3 1 : 3 2 : 3 3
Priority Queue
Adaptors for Containers 80
// priorityQueue.cpp
...
#include <queue>
...
std::priority_queue<int> myPriorityQueue;
myPriorityQueue.push(3);
myPriorityQueue.push(1);
myPriorityQueue.push(2);
std::cout << myPriorityQueue.top() << std::endl; // 3
while (!myPriorityQueue.empty()){
std::cout << myPriorityQueue.top() << " ";
myPriorityQueue.pop();
} // 3 2 1
std::priority_queue<std::string, std::vector<std::string>,
std::greater<std::string>> myPriorityQueue2;
myPriorityQueue2.push("Only");
myPriorityQueue2.push("for");
myPriorityQueue2.push("testing");
myPriorityQueue2.push("purpose");
myPriorityQueue2.push(".");
while (!myPriorityQueue2.empty()){
3 http://en.cppreference.com/w/cpp/container/priority_queue
Adaptors for Containers 81
Categories
Their capabilities can categorise iterator. C++ has forward, bidirectional and random access iterators.
With the forward iterator, you can iterate the container forward, with the bidirectional iterator, in
both directions. With the random access iterator, you can directly access an arbitrary element. In
particular, this means for the last one, that you can use iterator arithmetic and ordering comparisons
(e.g.: <). The category of an iterator depends on the type of the container used.
In the table below is a representation of containers and their iterator categories. The bidirectional
iterator includes the forward iterator functionalities, and the random access iterator includes the
forward and the bidirectional iterator functionalities. It and It2 are iterators, n is a natural number.
1 http://en.cppreference.com/w/cpp/header/iterator
Iterators 83
The input iterator and the output iterator are special forward iterators: they can read and write their
pointed element only once.
Iterator Creation
Each container generates its suitable iterator on request. For example an std::unordered_map
generates constant and non-constant forward iterators.
std::map<std::string, int>::const_reverse_iterator
mapIt= map.rcbegin();
auto mapIt2= map.rcbegin();
Useful Functions
The global functions std::begin, std::end, std::prev, std::next, std::distance and std::advance
make your handling of the iterators a lot easier. Only the function std::prev requires a bidirectional
iterator. All functions need the header <iterator>. The table gives you the overview.
Iterators 85
// iteratorUtilities.cpp
...
#include <iterator>
...
using std::cout;
for (auto m: myMap) cout << "{" << m.first << "," << m.second << "} ";
// {Juliette,1997},{Marius,1999},{Beatrix,1966},{Rainer,1966}
std::advance(arrIt, -5);
cout << *arrIt; // 4
Adaptors
Iterator adaptors enable the use of iterators in insert mode or with streams. They need the header
<iterator>.
Insert iterators
With the three insert iterators std::front_inserter, std::back_inserter and std::inserter
you can insert an element into a container at the beginning, at the end or an arbitrary position
respectively. The memory for the elements will automatically be provided. The three map their
functionality on the underlying methods of the container cont.
The table below gives you two pieces of information: Which methods of the containers are internally
used and which iterators can be used depends on the container’s type.
You can combine the algorithms in the STL with the three insert iterators.
#include <iterator>
...
std::deque<int> deq{5, 6, 7, 10, 11, 12};
std::vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
std::copy(vec.rbegin()+11, vec.rend(),
std::front_inserter(deq));
for (auto d: deq) std::cout << d << " ";
// 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Stream Iterators
Stream iterator adaptors can use streams as a data source or data sink. C++ offers two functions
to create istream iterators and two to create ostream iterators. The created istream iterators behave
like input iterators, the ostream iterators like insert iterators.
Function Description
std::istream_iterator<T> Creates an end-of-stream iterator.
std::istream_iterator<T>(istream) Creates an istream iterator for istream.
std::ostream_iterator<T>(ostream) Creates an ostream iterator for ostream
std::ostream_iterator<T>(ostream, delim) Creates an ostream iterator for ostream with the delimiter
delim.
Thanks to the stream iterator adapter you can directly read from or write to a stream.
Iterators 88
The following interactive program fragment reads in an endless loop natural numbers from std::cin
and pushes them onto the vector myIntVec. If the input is not a natural number, an error in the input
stream will occur. All numbers in myIntVec are copied to std::cout, separated by :. Now you can
see the result on the console.
#include <iterator>
...
std::vector<int> myIntVec;
std::istream_iterator<int> myIntStreamReader(std::cin);
std::istream_iterator<int> myEndIterator;
// Possible input
// 1
// 2
// 3
// 4
// z
while(myIntStreamReader != myEndIterator){
myIntVec.push_back(*myIntStreamReader);
++myIntStreamReader;
}
std::copy(myIntVec.begin(), myIntVec.end(),
std::ostream_iterator<int>(std::cout, ":"));
// 1:2:3:4:
8. Callable Units
This chapter is intentionally not exhaustive
This book is about the C++ Standard library, therefore, will no go into the details of callable
units. I provide only as much information as it’s necessary to use them correctly in the
algorithms of the Standard Template Library. An exhaustive discussion of callable units
should be part of a book about the C++ core language.
Many of the STL algorithms and containers can be parametrised with callable units or short callables.
A callable is something that behaves like a function. Not only are these functions but also function
objects and lambda functions. Predicates are special functions that return a boolean as the result.
If a predicate has one argument, it’s called a unary predicate if a predicate has two arguments, it’s
called a binary predicate. The same holds for functions. A function taking one argument is a unary
function; a function taking two arguments is a binary function.
Functions
Functions are the simplest callables. They can have - apart from static variables - no state. Because
the definition of a function is often widely separated from its use or even in a different translation
unit, the compiler has fewer opportunities to optimise the resulting code.
Function Objects
At first, don’t call them functors1 . That’s a well-defined term from the category theory.
Function objects2 are objects that behave like functions. They achieve this due to their call operator
being implemented. As functions objects are objects, they can have attributes and therefore state.
struct Square{
void operator()(int& i){i= i*i;}
};
There are function objects in the Standard Template Library for arithmetic, logic and bitwise
operations, and also for negation and comparison.
1 https://en.wikipedia.org/wiki/Functor
2 http://en.cppreference.com/w/cpp/utility/functional
Callable Units 91
Logical std::logical_not<T>()
std::logical_and<T>(), std::logical_or<T>()
Lambda Functions
Lambda functions3 provide in-place functionality. The compiler gets a lot of insight and has therefore
great optimisation potential. Lambda functions can receive their arguments by value or by reference.
They can capture their environment by value, by reference and with C++14 by move.
3 http://en.cppreference.com/w/cpp/language/lambda
9. Algorithms
The Standard Template Library has a large number of algorithms1 to work with containers and
their elements. As the algorithms are function templates, they are independent of the type of
the container elements. The glue between the containers and algorithms are the iterators. If your
container supports the interface of an STL container, you can apply the algorithms on your container.
Generic programming with the algorithmn
// algorithm.cpp
...
#include <algorithm>
...
template <typename Cont, typename T>
void doTheSame(Cont cont, T t){
for (const auto c: cont) std::cout << c << " ";
std::cout << cont.size() << std::endl;
std::reverse(cont.begin(), cont.end());
for (const auto c: cont) std::cout << c << " ";
std::reverse(cont.begin(), cont.end());
for (const auto c: cont) std::cout << c << " ";
auto It= std::find(cont.begin(), cont.end(), t);
std::reverse(It, cont.end());
for (const auto c: cont) std::cout << c << " ";
}
doTheSame(myVec, 5);
// 1 2 3 4 5 6 7 8 9 10
// 10
// 10 9 8 7 6 5 4 3 2 1
// 1 2 3 4 5 6 7 8 9 10
// 1 2 3 4 10 9 8 7 6 5
doTheSame(myDeq, "D");
// A B C D E F G H I
1 http://en.cppreference.com/w/cpp/algorithm
Algorithms 93
// 9
// I H G F E D C B A
// A B C D E F G H I
// A B C I H G F E D
doTheSame(myList, 'd');
// a b c d e f g h
// 8
// h g f e d c b a
// a b c d e f g h
// a b c h g f e d
Conventions
To use the algorithms, you have to keep a few rules in your head.
The algorithms are defined in various headers.
<algorithm>
Contains the general algorithms.
<numeric>
Contains the numeric algorithms.
Many of the algorithms have the name suffix _if and _copy.
Algorithms like auto num= std::count(InpIt first, InpIt last, const T& val) return the
number of elements that are equal to val. num is of type iterator_traits<InpIt>::difference_-
type. You have the guarantee that num is sufficient to hold the result. Because of the automatic return
type deduction with auto, the compiler will give you the right types.
Name Description
InIt Input iterator
FwdIt Forward iterator
BiIt Bidirectional iterator
Execution Policies
The policy tag specifies whether an algorithm should run sequentially, in parallel, or in parallel with
vectorisation.
// sequential execution
std::sort(std::execution::seq, v.begin(), v.end());
The example shows that you can still use the classic variant of std::sort without execution policy.
In addition, in C++17 you can specify explicitly whether the sequential, parallel, or the parallel and
vectorised version should be used.
3 https://en.wikipedia.org/wiki/SIMD
Algorithms 96
int main(){
for (int i = 0; i < SIZE; ++i) {
res[i] = vec[i] + 5;
}
}
The expression res[i] = vec[i] + 5 is the key line in this small example. Thanks
to the compiler Explorer4 we can have a closer look at the assembler instructions
generated by x86-64 clang 3.6.
Without Optimisation
Here are the assembler instructions. Each addition is done sequentially.
4 https://godbolt.org/
Algorithms 97
5 http://stellar.cct.lsu.edu/projects/hpx/
Algorithms 98
for_each
std::for_each applies a unary callable to each element of its range. The range is given by the input
iterators.
std::for_each when used without an explicit execution policy is a special algorithm because it
returns its callable argument. If you invoke std::for_each with a function object, you can store the
result of the function call directly in the function object.
std::for_each_n is new with C++17 and applies a unary callable to the first n elements of its range.
The range is given by an input iterator and a size.
std::for_each
// forEach.cpp
...
#include <algorithm>
...
template <typename T>
class ContInfo{
public:
void operator()(T t){
num++;
sum+= t;
}
int getSum() const{ return sum; }
int getSize() const{ return num; }
double getMean() const{
return static_cast<double>(sum)/static_cast<double>(num);
}
private:
T sum{0};
int num{0};
};
std::vector<double> myVec{1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9};
auto vecInfo= std::for_each(myVec.begin(), myVec.end(), ContInfo<double>());
Algorithms 99
Non-Modifying Algorithms
Non-modifying algorithms are algorithms for searching and counting elements. However, you can
also check properties on ranges, compare ranges or search for ranges within ranges.
Search Elements
You can search for elements in three different ways.
Returns an element in a range:
The algorithms require input or forward iterators as arguments and return an iterator on the element
when successfully found. If the search is not successful, they return an end iterator.
std::find, std::find_if, std::find_if_not, std::find_of, and std::adjacent_fint
// find.cpp
...
#include <algorithm>
...
using namespace std;
list<char> myCha{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'};
int cha[]= {'A', 'B', 'C'};
Count Elements
You can count elements with the STL with and without a predicate.
Returns the number of elements:
Count algorithms take input iterators as arguments and return the number of elements matching
val or the predicate.
// allAnyNone.cpp
...
#include <algorithm>
...
auto even= [](int i){ return i%2; };
std::vector<int> myVec{1, 2, 3, 4, 5, 6, 7, 8, 9};
std::cout << std::any_of(myVec.begin(), myVec.end(), even); // true
std::cout << std::all_of(myVec.begin(), myVec.end(), even); // false
std::cout << std::none_of(myVec.begin(), myVec.end(), even); // false
Compare Ranges
With std::equal you can compare ranges on equality. With std::lexicographical_compare and
std::mismatch you discover which range is the smaller one.
Finds the first position at which both ranges are not equal:
The algorithms take input iterators and eventually a binary predicate. std::mismatch returns as its
result a pair pa of input iterators. pa.first holds an input iterator for the first element that is not
equal. pa.second holds the corresponding input iterator for the second range. If both ranges are
identical, you get two end iterators.
std::equal, std::lexicographical_compare, and std::mismatch
// equalLexicographicalMismatch.cpp
...
#include <algorithm>
...
using namespace std;
Searches the second range in the first one and returns the positions. Starts at the end:
FwdIt search_n(FwdIt first, FwdIt last, Size count, const T& value)
FwdIt search_n(ExePol pol, FwdIt first, FwdIt last, Size count, const T& value)
FwdIt search_n(FwdIt first, FwdIt last, Size count, const T& value, BiPre pre)
FwdIt search_n(ExePol pol, FwdIt first,
FwdIt last, Size count, const T& value, BiPre pre)
// search.cpp
...
#include <algorithm>
...
using std::search;
Modifying Algorithms
C++ has many algorithms to modify elements and ranges.
Copies n elements:
The algorithms need input iterators and copy their elements to result. They return an end iterator
to the destination range.
Copy elements and ranges
// copy.cpp
...
#include <algorithm>
...
std::string str{"abcdefghijklmnop"};
std::string str2{"---------------------"};
void replace(FwdIt first, FwdIt last, const T& old, const T& newValue)
void replace(ExePol pol, FwdIt first, FwdIt last, const T& old,
const T& newValue)
Replaces the old elements of the range with newValue, if the old element fulfils the predicate pred:
void replace_if(FwdIt first, FwdIt last, UnPred pred, const T& newValue)
void replace_if(ExePol pol, FwdIt first, FwdIt last, UnPred pred,
const T& newValue)
Replaces the old elements in the range with newValue, if the old element has the value old. Copies
the result to result:
OutIt replace_copy(InpIt first, InpIt last, OutIt result, const T& old,
const T& newValue)
FwdIt2 replace_copy(ExePol pol, FwdIt first, FwdIt last,
FwdIt2 result, const T& old, const T& newValue)
Replaces the old elements of the range with newValue, if the old element fulfils the predicate pred.
Copies the result to result:
// replace.cpp
...
#include <algorithm>
...
std::string str2;
std::replace_copy(str.begin(), str.end(), std::back_inserter(str2), '2', '3');
std::cout << str2; // Only3for3testing3purpose.
std::string str3;
std::replace_copy_if(str2.begin(), str2.end(),
std::back_inserter(str3), [](char c){ return c == '3'; }, '4');
std::cout << str3; // Only4for4testing4purpose.
Removes the elements from the range, fulfilling the predicate pred:
Removes the elements from the range, having the value val. Copies the result to result:
OutIt remove_copy(InpIt first, InpIt last, OutIt result, const T& val)
FwdIt2 remove_copy(ExePol pol, FwdIt first, FwdIt last,
FwdIt2 result, const T& val)
Removes the elements from the range, which fulfil the predicate pred. Copies the result to result.
The algorithms need input iterators for the source range and an output iterator for the destination
range. They return as a result an end iterator for the destination range.
Algorithms 110
myVec.erase(newIt, myVec.end());
for (auto v: myVec) std::cout << v << " "; // 0 2 4 6 8
The algorithms expect the value val or a generator gen as an argument. gen has to be a function
taking no argument and returning the new value. The return value of the algorithms std::fill_n
and std::generate_n is an output iterator, pointing to the last created element.
Fill and create ranges
// fillAndCreate.cpp
...
#include <algorithm>
...
int getNext(){
static int next{0};
return ++next;
}
std::vector<int> vec(10);
std::fill(vec.begin(), vec.end(), 2011);
for (auto v: vec) std::cout << v << " ";
// 2011 2011 2011 2011 2011 2011 2011 2011 2011 2011
std::generate_n(vec.begin(), 5, getNext);
for (auto v: vec) std::cout << v << " ";
// 1 2 3 4 5 2011 2011 2011 2011 2011
Move Ranges
std::move moves the ranges forward; std::move_backward moves the ranges backwards.
Both algorithms need a destination iterator result, to which the range is moved. In the case of the
std::move algorithm this is an output iterator, and in the case of the std::move_backward algorithm
this is a bidirectional iterator. The algorithms return an output or bidirectional iterator, pointing to
the initial position in the destination range.
Move ranges
// move.cpp
...
#include <algorithm>
...
std::string str{"abcdefghijklmnop"};
std::string str2{"---------------------"};
std::move_backward(str.begin(), str.end(), str2.end());
std::cout << str2; // -----abcdefghijklmnop
Swap Ranges
std::swap and std::swap_ranges can swap objects and ranges.
Swaps objects:
Swaps ranges:
The returned iterator points to the last swapped element in the destination range.
Swap algorithms
// swap.cpp
...
#include <algorithm>
...
std::string str{"abcdefghijklmnop"};
std::string str2{"---------------------"};
std::swap_ranges(str.begin(), str.begin()+5, str2.begin()+5);
std::cout << str << std::endl; // -----fghijklmnop
std::cout << str2 << std::endl; // -----abcde-----------
Transform Ranges
The std::transform algorithm applies a unary or binary callable to a range and copies the modified
elements to the destination range.
Applies the unary callable fun to the elements of the input range and copies the result to result:
Applies the binary callable fun to both input ranges and copies the result to result:
The difference between the two versions is that the first version applies the callable to each element
of the range; the second version applies the callable to pairs of both ranges in parallel. The returned
iterator points to one position after the last transformed element.
Algorithms 114
Transform algorithms
// transform.cpp
...
#include <algorithm>
...
std::string str{"abcdefghijklmnopqrstuvwxyz"};
std::transform(str.begin(), str.end(), str.begin(),
[](char c){ return std::toupper(c); });
std::cout << str; // ABCDEFGHIJKLMNOPQRSTUVWXYZ
Reverse Ranges
std::reverse and std::reverse_copy invert the order of the elements in their range.
Reverses the order of the elements in the range and copies the result to result:
Both algorithms require bidirectional iterators. The returned iterator points to the position of the
output range result before the elements were copied.
Algorithms 115
// algorithmen.cpp
...
#include <algorithm>
...
std::string str{"123456789"};
std::reverse(str.begin(), str.begin()+5);
std::cout << str; // 543216789
Rotate Ranges
std::rotate and std::rotate_copy rotate their elements.
Rotates the elements in such a way that middle becomes the new first element:
Rotates the elements in such a way that middle becomes the new first element. Copies the result to
result:
Both algorithms need forward iterators. The returned iterator is an end iterator for the copied range.
Rotate algorithms
// rotate.cpp
...
#include <algorithm>
...
std::string str{"12345"};
for (auto i= 0; i < str.size(); ++i){
std::string tmp{str};
std::rotate(tmp.begin(), tmp.begin()+i , tmp.end());
std::cout << tmp << " ";
} // 12345 23451 34512 45123 51234
Algorithms 116
Randomly shuffles the elements in the range, by using the random number generator gen:
Randomly shuffles the elements in a range, using the uniform random number generator gen:
The algorithms need random access iterators. RanNumGen&& gen has to be a callable, taking an
argument and returning a value within its arguments. URNG&& gen has to be a uniform random
number generator.
Prefer std::shuffle
Use std::shuffle instead of std::random_shuffle. std::random_shuffle has been depre-
cated since C++14 and remoed in C++17, because it uses the C function rand internally.
using std::chrono::system_clock;
using std::default_random_engine;
std::vector<int> vec1{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
std::vector<int> vec2(vec1);
std::random_shuffle(vec1.begin(), vec1.end());
for (auto v: vec1) std::cout << v << " "; // 4 3 7 8 0 5 2 1 6 9
Remove Duplicates
With the algorithms std::unique and std::unique_copy you have more opportunities to remove
adjacent duplicates. This can be done with and without a binary predicate.
Removes adjacent duplicates:
Removes adjacent duplicates, satisfying the binary predicate and copies the result to result:
// removeDuplicates.cpp
...
#include <algorithm>
...
std::vector<int> myVec{0, 0, 1, 1, 2, 2, 3, 4, 4, 5,
3, 6, 7, 8, 1, 3, 3, 8, 8, 9};
std::vector<int> myVec2{1, 4, 3, 3, 3, 5, 7, 9, 2, 4, 1, 6, 8,
0, 3, 5, 7, 8, 7, 3, 9, 2, 4, 2, 5, 7, 3};
std::vector<int> resVec;
resVec.reserve(myVec2.size());
std::unique_copy(myVec2.begin(), myVec2.end(), std::back_inserter(resVec),
[](int a, int b){ return (a%2) == (b%2); } );
for(auto v: myVec2) std::cout << v << " ";
// 1 4 3 3 3 5 7 9 2 4 1 6 8 0 3 5 7 8 7 3 9 2 4 2 5 7 3
for(auto v: resVec) std::cout << v << " "; // 1 4 3 2 1 6 3 8 7 2 5
Partition
What is a partition?
A partition of a set is a decomposition of a set in subsets so that each element of the set
is precisely in one subset. The subsets are defined in C++ by a unary predicate so that the
members of the first subset fulfil the predicate. The remaining elements are in the second
subset.
C++ offers a few functions for dealing with partitions. All of them need a unary predicate pre.
std::partition and std::stable_partition partition a range and returns the partition point. With
std::partition_point you can get the partition point of a partition. Afterwards you can check the
partition with std::is_partitioned or copy it with std::partition_copy.
Checks if the range is partitioned:
Algorithms 119
Partition algorithms
// partition.cpp
...
#include <algorithm>
...
using namespace std;
for (auto v= vec.begin(); v != parPoint; ++v) cout << *v << " ";
// 1 7 3 3 5 9 7 3 3 5 5
for (auto v= parPoint; v != vec.end(); ++v) cout << *v << " ";
// 4 8 4 6 6 6 8 8 4 6 4 4 6 4 8
Sort
You can sort a range with std::sort or std::stable_sort or sort until a position with std::partial_-
sort. In addition std::partial_sort_copy copies the partially sorted range. With std::nth_element
you can assign an element the sorted position in the range. You can check with std::is_sorted if
a range is sorted. If you want to know until which position a range is sorted, use std::is_sorted_-
until.
Per default the predefined function object std::less is used a as sorting criterion. However, you
can use your sorting criterion. This has to obey the strict weak ordering.
Sorts the elements in the range:
Sorts partially the elements in the range and copies them in the destination ranges result_first
and result_last:
Returns the position to the first element that doesn’t satisfy the sorting criterion:
Algorithms 122
Reorders the range, so that the n-th element has the right (sorted) position:
// sort.cpp
...
#include <algorithm>
...
std::string str{"RUdAjdDkaACsdfjwldXmnEiVSEZTiepfgOIkue"};
std::cout << std::is_sorted(str.begin(), str.end()); // false
Binary Search
The binary search algorithms use the fact that the ranges are already sorted. To search for an element,
use std::binary_search. With std::lower_bound you get an iterator for the first element, being no
smaller than the given value. With std::upper_bound you get an iterator back for the first element,
which is bigger than the given value. std:::equal_range combines both algorithms.
If the container has n elements, you need on average log2(n) comparisons for the search. The binary
search requires that you use the same comparison criterion that you used for sorting the container.
Per default the comparison criterion is std::less, but you can adjust it. Your sorting criterion has
to obey the strict weak ordering. If not, the program is undefined.
If you have an unordered associative container, the methods of the unordered associative container
are in general faster.
Searches the element val in the range:
Returns the position of the first element of the range, being not smaller than val:
Returns the position of the first element of the range, being bigger than val:
Returns the pair std::lower_bound and std::upper_bound for the element val:
// binarySearch.cpp
...
#include <algorithm>
...
using namespace std;
Merge Operations
Merge operations empower you to merge sorted ranges in a new sorted range. The algorithm requires
that the ranges and the algorithm use the same sorting criterion. If not, the program is undefined.
Per default the predefined sorting criterion std::less is used. If you use your sorting criterion, it
has to obey the strict weak ordering. If not, the program is undefined.
You can merge two sorted ranges with std::inplace_merge and std::merge. You can check with
std::includes if one sorted range is in another sorted range. You can merge with std::set_-
difference, std::set_intersection, std::set_symmetric_difference and std::set_union two
sorted ranges in a new sorted range.
Merges in place two sorted sub ranges [first, mid) and [mid, last):
Algorithms 125
OutIt merge(InpIt first1, InpIt last1, InpIt first2, InpIt last2, OutIt result)
FwdIt3 merge(ExePol pol, FwdIt1 first1, FwdIt1 last1,
FwdIt2 first2, FwdIt2 last2, FwdIt3 result)
OutIt merge(InpIt first1, InpIt last1, InpIt first2, InpIt last2, OutIt result,
BiPre pre)
FwdIt3 merge(ExePol pol, FwdIt1 first1, FwdIt1 last1,
FwdpIt2 first2, FwdIt2 last2, FwdIt3 result, BiPre pre)
Checks if all elements of the second range are in the first range:
bool includes(InpIt first1, InpIt last1, InpIt first2, InpIt last2, BinPre pre)
bool includes(ExePol pol, FwdIt first1, FwdIt last1,
FwdIt1 first2, FwdIt1 last2, BinPre pre)
Copies these elements of the first range to result, being not in the second range:
Determines the intersection of the first with the second range and copies the result to result:
Algorithms 126
Determines the symmetric difference of the first with the second range and copies the result to
result:
Determines the union of the first with the second range and copies the result to result:
The returned iterator is an end iterator for the destination range. The destination range of
std::set_difference has all the elements in the first, but not the second range. On the contrary,
the destination range of std::symmetric_difference has only the elements that are elements of one
range, but not both. std::union determines the union of both sorted ranges.
Algorithms 127
Merge algorithms
// merge.cpp
...
#include <algorithm>
...
std::sort(vec1.begin(), vec1.end());
std::vector<int> vec(vec1);
vec1.reserve(vec1.size() + vec2.size());
vec1.insert(vec1.end(), vec2.begin(), vec2.end());
for (auto v: vec1) std::cout << v << " "; // 1 1 2 3 4 5 6 7 8 9 1 2 3
vec2.push_back(10);
for (auto v: vec) std::cout << v << " "; // 1 1 2 3 4 5 6 7 8 9
for (auto v: vec2) std::cout << v << " "; // 1 2 3 10
std::vector<int> res;
std::set_symmetric_difference(vec.begin(), vec.end(), vec2.begin(), vec2.end(),
std::back_inserter(res));
for (auto v : res) std::cout << v << " "; // 1 4 5 6 7 8 9 10
res= {};
std::set_union(vec.begin(), vec.end(), vec2.begin(), vec2.end(),
std::back_inserter(res));
for (auto v : res) std::cout << v << " "; // 1 1 2 3 4 5 6 7 8 9 10
Algorithms 128
Heaps
What is a heap?
A heap is a binary search tree in which parent elements are always bigger than its child
elements. Heap trees are optimised for the efficient sorting of elements.
You can create with std::make_heap a heap. You can push with std::push_heap new elements on
the heap. On the contrary, you can pop the largest element with std::pop_heap from the heap. Both
operations respect the heap characteristics. std::push_heap moves the last element of the range on
the heap; std::pop_heap moves the biggest element of the heap to the last position in the range.
You can check with std::is_heap if a range is a heap. You can determine with std::is_heap_until
until which position the range is a heap. std::sort_heap sorts the heap.
The heap algorithms require that the ranges and the algorithm use the same sorting criterion. If not,
the program is undefined. Per default the predefined sorting criterion std::less is used. If you use
your sorting criterion, it has to obey the strict weak ordering. If not, the program is undefined.
Creates a heap from the range:
Algorithms 129
Pushes the last element of the range onto the heap. [first, last-1) has to be a heap.
Removes the biggest element from the heap and puts it to the end of the range:
Withstd::pop_heap you can remove the biggest element from the heap. Afterwards, the biggest
element is the last element of the range. To remove it from the heap h, use h.pop_back.
Algorithms 130
Heap algorithms
// heap.cpp
...
#include <algorithm>
...
vec.push_back(100);
std::cout << std::is_heap(vec.begin(), vec.end()); // false
std::cout << *std::is_heap_until(vec.begin(), vec.end()); // 100
for (auto v: vec) std::cout << v << " "; // 10 9 7 4 5 6 2 3 1 100
std::push_heap(vec.begin(), vec.end());
std::cout << std::is_heap(vec.begin(), vec.end()); // true
for (auto v: vec) std::cout << v << " "; // 100 10 7 4 9 6 2 3 1 5
std::pop_heap(vec.begin(), vec.end());
for (auto v: vec) std::cout << v << " "; // 10 9 7 4 5 6 2 3 1 100
std::cout << *std::is_heap_until(vec.begin(), vec.end()); // 100
vec.resize(vec.size()-1);
std::cout << std::is_heap(vec.begin(), vec.end()); // true
std::cout << vec.front() << std::endl; // 10
If the range has more than one minimum or maximum element, the first one is returned.
Minimum and maximum algorithms
// minMax.cpp
...
#include <algorithm>
...
Permutations
std::prev_permutation and std::next_permutation return the previous smaller or next bigger
permutation of the newly ordered range. If a smaller or bigger permutation is not available, the
algorithms return false. Both algorithms need bidirectional iterators. Per default the predefined
sorting criterion std::less is used. If you use your sorting criterion, it has to obey the strict weak
ordering. If not, the program is undefined.
Applies the previous permutation to the range:
You can easily generate with both algorithms all permutations of the range.
Permutation algorithms
// permutation.cpp
...
#include <algorithm>
...
std::reverse(myInts.begin(), myInts.end());
Algorithms 133
do{
for (auto i: myInts) std::cout << i;
std::cout << " ";
} while(std::prev_permutation(myInts.begin(), myInts.end()));
// 321 312 231 213 132 123
Numeric
The numeric algorithms std::accumulate, std::adjacent_difference, std::partial_sum, std::inner_-
product and std::iota and the six additional C++17 algorithms std::exclusive_scan, std::inclusive_-
scan, std::transform_exclusive_scan, std::transform_inclusive_scan, std::reduce, and std::transform_-
reduce are special. All of them are defined in the header <numeric>. They are widely applicable,
because they can be configured with a callable.
Accumulates the elements of the range. init is the start value:
Calculates the difference between adjacent elements of the range and stores the result in result:
Calculates the inner product (scalar product) of the two ranges and returns the result:
Assigns each element of the range a by 1 sequentially increasing value. The start value is val:
Algorithms 134
result = init;
result += *(first+0);
result += *(first+1);
...
*(result) = *first;
*(result+1) = *(first+1) - *(first);
*(result+2) = *(first+2) - *(first+1);
...
*(result) = *first;
*(result+1) = *first + *(first+1);
*(result+2) = *first + *(first+1) + *(first+2)
...
The challenging algorithm variation inner_product(InpIt, InpIt, OutIt, T, BiFun fun1, BiFun
fun2) with two binary callables uses the following strategy: The second callable fun2 is applied
to each pair of the ranges to generate the temporary destination range tmp, and the first callable is
applied to each element of the destination range tmp for accumulating them and therefore generating
the final result.
Numeric algorithms
// numeric.cpp
...
#include <numeric>
...
std::vector<int> myVec;
std::adjacent_difference(vec.begin(), vec.end(),
std::back_inserter(myVec), [](int a, int b){ return a*b; });
for (auto v: myVec) std::cout << v << " "; // 1 2 6 12 20 30 42 56 72
std::cout << std::inner_product(vec.begin(), vec.end(), arr.begin(), 0); // 285
myVec= {};
std::partial_sum(vec.begin(), vec.end(), std::back_inserter(myVec));
for (auto v: myVec) std::cout << v << " "; // 1 3 6 10 15 21 28 36 45
std::vector<int> myLongVec(10);
std::iota(myLongVec.begin(), myLongVec.end(), 2000);
for (auto v: myLongVec) std::cout << v << " ";
// 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009
transform_reduce: transforms and reduces the elements of one or two ranges. init is the start value.
MapReduce in C++17
The Haskell7 function map is called std::transform in C++. When you substitute transform
with map in the name std::transform_reduce, you will get std::map_reduce. MapReduce8 is
the well-known parallel framework that first maps each value to a new value, then reduces
in the second phase all values to the result.
The algorithm is directly applicable in C++17. In the following example, in the map phase,
each word is mapped to its length, and the lengths of all words are then reduced to their
sum during the reduce phase. The result is the sum of the length of all words.
7 https://www.haskell.org/
8 https://en.wikipedia.org/wiki/MapReduce
Algorithms 137
OutIt exclusive_scan(InpIt first, InpIt last, OutIt first, T init, BiFun fun)
FwdIt2 exclusive_scan(ExePol pol, FwdIt first, FwdIt last,
FwdIt2 first2, T init, BiFun fun)
OutIt inclusive_scan(InpIt first, InpIt last, OutIt firs2t, BiFun fun, T init)
FwdIt2 inclusive_scan(ExePol pol, FwdIt first, FwdIt last,
FwdIt2 first2, BiFun fun, T init)
transform_exclusive_scan: first transforms each element and then computes the exclusive prefix
sums
transform_inclusive_scan: first transforms each element of the input range and then computes the
inclusive prefix sums
Algorithms 138
The following example illustrates the usage of the six algorithms using the parallel execution policy.
The new algorithms
// newAlgorithms.cpp
...
#include <numeric>
...
for (auto v: resVec) std::cout << v << " "; // 1 1 2 6 24 120 720 5040
std::inclusive_scan(std::execution::par,
resVec2.begin(), resVec2.end(), resVec2.begin(),
[](int fir, int sec){ return fir * sec; }, 1);
for (auto v: resVec2) std::cout << v << " "; // 1 2 6 24 120 720 5040 40320
resVec3.begin(), resVec3.end(),
resVec4.begin(), 0,
[](int fir, int sec){ return fir + sec; },
[](int arg){ return arg *= arg; });
std::transform_inclusive_scan(std::execution::par,
strVec.begin(), strVec.end(),
resVec5.begin(),
[](auto fir, auto sec){ return fir + sec; },
[](auto s){ return s.length(); }, 0);
Random Numbers
Random numbers1 are necessary for many domains, e.g., to test software, to generate cryptographic
keys or for computer games. The random number facility of C++ consists of two components. There
is the generation of the random numbers, and there is the distribution of these random numbers.
Both components need the header <random>.
#include <random>
...
std::random_device seed;
std::mt19937 generator(seed());
A random number generator gen of type Generator supports four different requests:
Generator::result_type
Data type of the generated random number.
gen()
Returns a random number.
gen.min()
Returns the minimum random number that can be returned by gen().
gen.max()
Returns the maximum random number that can be returned by gen.
The random number library supports several random number generators. The best known are the
Mersenne Twister, the std::default_random_engine that is chosen by the implementation and
std::random_device. std::random_device is the only true random number generator, but not all
platforms offer it.
1 http://en.cppreference.com/w/cpp/header/random
Numeric 141
#include <random>
...
std::random_device seed;
std::mt19937 gen(seed());
std::uniform_int_distribution<> unDis(0, 20); // distribution between 0 and 20
unDis(gen); // generates a random number
C++ has several discrete and continuous random number distributions. The discrete random number
distribution generates integers, the continuous random number distribution generates floating point
numbers.
class bernoulli_distribution;
template<class T = int> class uniform_int_distribution;
template<class T = int> class binomial_distribution;
template<class T = int> class geometric_distribution;
template<class T = int> class negative_binomial_distribution;
template<class T = int> class poisson_distribution;
template<class T = int> class discrete_distribution;
template<class T = double> class exponential_distribution;
template<class T = double> class gamma_distribution;
template<class T = double> class weibull_distribution;
template<class T = double> class extreme_value_distribution;
template<class T = double> class normal_distribution;
template<class T = double> class lognormal_distribution;
template<class T = double> class chi_squared_distribution;
template<class T = double> class cauchy_distribution;
template<class T = double> class fisher_f_distribution;
template<class T = double> class student_t_distribution;
template<class T = double> class piecewise_constant_distribution;
template<class T = double> class piecewise_linear_distribution;
template<class T = double> class uniform_real_distribution;
Class templates with a default template argument int are discrete. The Bernoulli distribution
generates booleans.
Here is an example using the Mersenne Twister std::mt19937 as the pseudo random-number
generator for generating 1 million random numbers. The random number stream is mapped to the
uniform and normal (or Gaussian) distribution.
Numeric 142
Random numbers
// random.cpp
...
#include <random>
...
The following pictures show the uniform and the normal distribution of the 1 million random
numbers as a plot.
Numeric 143
Additionally, C++ inherits further mathematical functions from C. They are defined in the header
<cstdlib>3 . Once more, the names.
2 http://en.cppreference.com/w/cpp/numeric/math
3 http://en.cppreference.com/w/cpp/numeric/math
Numeric 144
All functions for integers are available for the types int, long and long long; all functions for
floating point numbers are available for the types float, double and long double.
The numeric functions need to be qualified with the namespace std.
Mathematic functions
// mathFunctions.cpp
...
#include <cmath>
#include <cstdlib>
...
double intPart;
auto fracPart= std::modf(5.7, &intPart);
std::cout << intPart << " + " << fracPart; // 5 + 0.7
std::div_t divresult= std::div(14, 5);
std::cout << divresult.quot << " " << divresult.rem; // 2 4
// seed
std::srand(time(nullptr));
for (int i= 0;i < 10; ++i) std::cout << (rand()%6 + 1) << " ";
// 3 6 5 3 6 5 6 3 1 5
11. Strings
A string1 is a sequence of characters. C++ has many methods to analyse or to change a string. C++-
strings are the safe replacement for C Strings: const char*. Strings need the header <string>.
std::string name{"RainerGrimm"};
auto strIt= std::find_if(name.begin()+1, name.end(),
[](char c){ return std::isupper(c); });
if (strIt != name.end()){
firstName= std::string(name.begin(), strIt);
lastName= std::string(strIt, name.end());
}
Strings are class templates parametrised by their character, their character trait and their allocator.
The character trait and the allocator have defaults.
1 http://en.cppreference.com/w/cpp/string/basic_string
Strings 146
C++ has synonyms for the character types char, wchar_t, char16_t and char32_t
Methods Example
Default std::string str
Destructor str.∼string()
Strings 147
Creation of a string
// stringConstructor.cpp
...
#include <string>
...
std::string defaultString;
std::string other{"123456789"};
std::string str1(other); // 123456789
std::string tmp(other); // 123456789
std::string str2(std::move(tmp)); // 123456789
std::string str3(other.begin(), other.end()); // 123456789
std::string str4(other, 2); // 3456789
std::string str5(other, 2, 5); // 34567
std::string str6("123456789", 5); // 12345
std::string str7(5, '1'); // 11111
std::string str8({'1', '2', '3', '4', '5'}); // 12345
std::cout << str6.substr(); // 12345
std::cout << str6.substr(1); // 2345
std::cout << str6.substr(1, 2); // 23
While the conversion of a C string in a C++-string id done implicitly, you must explicitly request the
conversion from a C++-string into a C string. str.copy() copies the content of a C++-string without
the terminating \0 character. str.data() and str.c_str() includes the terminating null character.
Strings 148
std::string str{"C++-String"};
str += " C-String";
std::cout << str; // C++-String C-String
const char* cString= str.c_str();
char buffer[10];
str.copy(buffer, 10);
str+= "works";
// const char* cString2= cString; // ERROR
std::string str2(buffer, buffer+10);
std::cout<< str2; // C++-String
The following table shows the methods for dealing with the memory management of the string.
Methods Description
str.empty() Checks if str has elements.
str.size(), str.length() Number of elements of the str.
str.capacity() Number of elements str can have without
reallocation.
str.max_size() Number of elements str can maximal have.
str.resize(n) Increases str to n elements.
Strings 149
Methods Description
std::string str;
showStringInfo(str); // "": 0 0 4611686018427387897
str +="12345";
showStringInfo(str); // "12345": 5 5 4611686018427387897
str.resize(30);
showStringInfo(str); // "12345": 30 30 4611686018427387897
str.reserve(1000);
showStringInfo(str); // "12345": 30 1000 4611686018427387897
str.shrink_to_fit();
showStringInfo(str); // "12345": 30 30 4611686018427387897
Comparison
Strings support the well-known comparison operators ==, !=, <, >, >=. The comparison of two
strings takes place on their elements.
Strings 150
String comparison
// stringComparisonAndConcatenation.cpp
...
#include <string>
...
std::string first{"aaa"};
std::string second{"aaaa"};
String Concatenation
The + operator is overloaded for strings, so you can add strings.
Element Access
The access to the elements of a string str is very convenient, because the string supports random
access iterators. You can access with str.front() the first character and with str.back() the last
character of the string. With str[n] and str.at(n) you get the n-th element by index.
The following table provides an overview.
Strings 151
Methods Example
str.front() Returns the first character of str.
str.back() Returns the last character of str.
str[n] Returns the n-th character of str. The string boundaries will not be
checked.
str.at(n) Returns the n-th character of str. The string boundaries will be
checked. If the boundaries are violated a std::out_of_range
exception is thrown.
Element access
// stringAccess.cpp
...
#include <string>
...
It is particularly interesting to see in the example that the compiler performs the invocation str[10].
The access outside the string boundaries is undefined behaviour. In contrary, the compiler complains
the call str.at(10).
Strings 152
getline consumes the whole line including empty spaces. Only the line separator is ignored. The
function needs the header <string>.
Input and output with strings
// stringInputOutput.cpp
...
#include <string>
...
std::string fileName;
std::cout << "Your filename: ";
std::cin >> fileName;
std::vector<std::string> lines= readFromFile(fileName.c_str());
int num{0};
for (auto line: lines) std::cout << ++num << ": " << line << std::endl;
Strings 153
The program displays the lines of an arbitrary file including their line number. The expression
std::cin >> fileName reads the file name. The function readFromFile reads with getline all file
lines and pushes them onto the vector.
Search
C++ offers the ability to search in a string in many variations. Each variation exists in various
overloaded forms.
The arguments of all six variations of the find functions follow a similar pattern. The first argument
is the text you are searching for. The second argument holds the start position of the search and the
third the number of characters starting from the second argument.
Here are the six variations.
Methods Description
str.find(...) Returns the first position of a character, a C or C++-string in str.
str.rfind(...) Returns the last position of a character, a C or C++-string in str.
str.find_first_of(...) Returns the first position of a character from a C or C++-string in str.
str.find_last_of(...) Returns the last position of a character from a C or C++-string in str.
str.find_first_not_of(...) Returns the first position of a character in str, which is not from a C or
C++-string.
str.find_last_not_of(...) Returns the last position of a character in str, which is not from a C or
C++-string.
Strings 154
Find(search) in a string
// stringFind.cpp
...
#include <string>
...
std::string str;
str= {"dkeu84kf8k48kdj39kdj74945du942"};
std::string str2{"84"};
str2="0123456789";
The call std::find(str2, 10) returns std::string::npos. If I display that value I get on my
platform 18446744073709551615.
Strings 155
Modifying Operations
Strings have many operations to modify them. str.assign assigns a new string to the string str.
With str.swap you can swap two strings. To remove a character from a string use str.pop_back or
str.erase. In contrary str.clear or str.erase deletes the whole string. To append new characters
to a string use +=, std.append or str.push_back. You can use str.insert to insert new characters
or str.replace to replace characters.
Methods Description
str= str2 Assigns str2 to str.
str.assign(...) Assigns to stra new string.
str.swap(str2) Swaps str and str2.
str.pop_back() Removes the last character from str.
str.erase(...) Removes characters from str.
str.clear() Clears the str.
str.append(...) Appends characters to str.
str.push_back(s) Appends the character s to str.
str.insert(pos, ...) Inserts characters in str starting at pos.
str.replace(pos, len, ...) Replaces the len characters from str starting at pos
The operations are available in many overloaded versions. The methods str.assign, str.append,
str.insert and str.replace are very similar. All four can be invoked with C++-strings and
substrings, but also characters, C strings, C string arrays, ranges and initialiser lists. str.erase can
erase a single character, ranges, but also many characters starting at a given position.
The following code snippets shows many of the variations. For simplicity reasons only the effects
of the strings modifications are displayed:
Modifying strings
// stringModification.cpp
...
#include <string>
...
str= {"0123456789"};
str.erase(7, 2); // 01234569
str.erase(str.begin()+2, str.end()-2); // 012
str.erase(str.begin()+2, str.end()); // 01
str.pop_back(); // 0
str.erase(); //
str= "01234";
str+= "56"; // 0123456
str+= '7'; // 01234567
str+= {'8', '9'}; // 0123456789
str.append(str); // 01234567890123456789
str.append(str, 2, 4); // 012345678901234567892345
str.append(3, '0'); // 012345678901234567892345000
str.append(str, 10, 10); // 01234567890123456789234500001234567989
str.push_back('9'); // 012345678901234567892345000012345679899
str= {"345"};
str.insert(3, "6789"); // 3456789
str.insert(0, "012"); // 0123456789
Numeric Conversions
You can convert with std::to_string(val) and std::to_wstring(val) numbers or floating point
numbers to the corresponding std::string or std::wstring. For the opposite direction for the
numbers or floating point numbers, you have the function family of the sto* functions. All functions
need the header <string>.
The sto functions all have the same interface. The example shows it for the type long.
The function takes a string and determines the long representation to the base base. stol ignores
leading spaces and optionally returns the index of the first invalid character in idx. By default, the
base is 10. Valid values for the base are 0 and 2 until 36. If you use base 0 the compiler automatically
determines the type based on the format of the string. If the base is bigger than 10 the compiler
encodes them in the characters a until z. The representation is analogous to the representation of
hexadecimal numbers.
The table gives the overview of all functions.
Strings 158
Method Description
std::to_string(val) Converts val into a std::string.
std::to_wstring(val) Converts val into a std::wstring.
std::stoi(str) Returns an int value.
std::stol(str) Returns a long value.
std::stoll(str) Returns a long long value.
std::stoul(str) Returns an unsigned long value.
std::stoull(str) Returns an unsigned long long value.
std::stof(str) Returns a float value.
std::stod(str) Returns a double value.
std::stold(str) Returns an long double value.
The functions throw a std::invalid_argument exception if the conversion is not possible. If the
determined value is too big for the destination type you get a std::out_of_range exception.
Numeric conversion
stringNumericConversion.cpp
...
#include <string>
...
std::string maxLongLongString=
std::to_string(std::numeric_limits<long long>::max());
std::wstring maxLongLongWstring=
std::to_wstring(std::numeric_limits<long long>::max());
std::string str("10010101");
std::cout << std::stoi(str); // 10010101
Strings 159
std::size_t idx;
std::cout << std::stod(" 3.5 km", &idx); // 3.5
std::cout << idx; // 6
try{
std::cout << std::stoi(" 3.5 km") << std::endl; // 3
std::cout << std::stoi(" 3.5 km", nullptr, 2) << std::endl;
}
catch (const std::exception& e){
std::cerr << e.what() << std::endl;
} // stoi
12. String Views
A string view1 is a non-owning reference to a string. It represents a view of a sequence of characters.
This sequence of characters can be a C++-string or a C-string. A string view needs the header
<string_view>.
String views are class templates parameterised by their character and their character trait. The
character trait has a default. In contrast to a string, a string view is non-owner and, therefore, needs
no allocator.
template<
class CharT,
class Traits = std::char_traits<CharT>
> class basic_string_view;
According to strings, there exist for string views four synonyms for the underlying character types
char, wchar_t, char16_t and char32_t.
1 http://en.cppreference.com/w/cpp/string/basic_string_view
String Views 161
Methods Example
Empty string view std::string_view str_view
Non-modifying operations
To make this chapter concise and not repeat the detailed descriptions from the chapter on strings,
I only mention the non-modifying operations of the string view. For further details, please use the
link to the associated documentation in the string chapter.
• Element access: operator[], at, front, back, data (see string: element access)
• Capacity: size, length, max_size, empty (see string: size versus capacity)
• Find: find, rfind, find_first_of, find_last_of, find_first_not_of, find_last_not_of
(see string: search)
• Copy: copy (see string: conversion between a C++-string and a C-String)
Modifying operations
The call stringView.swap(stringView2) swaps the content of the two string views. The methods
remove_prefix and remove_suffix are unique to a string view because a string supports neither.
remove_prefix shrinks its start forward; remove_suffix shrinks its end backwards.
String Views 162
Non-modifying operations
// string_view.cpp
...
#include <string_view>
...
...
void getStringView(std::string_view){}
getString(large);
getString("012345678901234567890"
"1234567890123456789"); // 41 bytes allocated
const char message []= "0123456789012345678901234567890123456789";
getString(message); // 41 bytes allocated
Thanks to the global overload operator new I can observe each memory allocation.
13. Regular Expressions
Regular expression1 is a language for describing text patterns. They need the header <regex>.
Regular expressions are a powerful tool for the following tasks:
C++ supports six different grammars for regular expressions. By default, the ECMAScript grammar
is used. This one is the most powerful grammar of the six grammars and is quite similar to the
grammar used in Perl 5. The other five grammars are the basic, extended, awk, grep and egrep
grammars.
#include <regex>
...
std::string regExpr("C\\+\\+");
std::string regExprRaw(R"(C\+\+)");
1 http://en.cppreference.com/w/cpp/regex
Regular Expressions 165
std::smatch result;
std::regex_search(text, result, rgx);
Character Types
The type of the text determines the character type of the regular expression, the type of the search
result and the type of the action with the search result.
The table shows the four different combinations.
The program shown in the Search section of this chapter provides the four combinations in detail.
You can further customise the object of type regular expression. Therefore you can specify the used
grammar or adapt the syntax. As said before, C++ supports the basic, extended, awk, grep and
egrep grammars. A regular expression qualified by the std::regex_constants::icase flag is case
insensitive. If you want to adopt the syntax, you have to specify the grammar explicitly.
Specify the grammar
// regexGrammar.cpp
...
#include <regex>
...
using std::regex_constants::ECMAScript;
using std::regex_constants::icase;
std::regex rgx(regExprStr);
std::smatch smatch;
If you use the case-sensitive regular expression rgx the result of the search in the text theQuestion
is c++. That’s not the case if your case-insensitive regular expression rgxIn is applied. Now you get
the match string C++.
Interface of std::smatch
Method Description
smatch.size() Returns the number of capture groups.
smatch.empty() Returns if the search result has a capture group.
smatch[i] Returns the ith capture group.
smatch.length(i) Returns the length of the ith capture group.
smatch.position(i) Returns the position of the ith capture group.
smatch.str(i) Returns the ith capture group as string.
smatch.prefix() and smatch.suffix() Returns the string before and after the capture group.
smatch.begin() and smatch.end() Returns the begin and end iterator for the capture groups.
smatch.format(...) Formats std::smatch objects for the output.
The following program shows the output of the first four capture groups for different regular
expressions.
Regular Expressions 168
Capture groups
// captureGroups.cpp
...
#include<regex>
...
using namspace std;
showCaptureGroups("abc+", "abccccc");
showCaptureGroups("(a+)(b+)", "aaabccc");
showCaptureGroups("((a+)(b+))", "aaabccc");
showCaptureGroups("(ab)(abc)+", "ababcabc");
...
std::sub_match
The capture groups are of type std::sub_match. As with std::match_results C++ defines the
following four type synonyms.
Method Description
cap.matched() Indicates if this match was successful.
cap.first() and cap.end() Returns the begin and end iterator of the character sequence.
cap.length() Returns the length of the capture group.
cap.str() Returns the capture group as string.
cap.compare(other) Compares the current capture group with another capture group.
Here is a code snippet showing the interplay between the search result std::match_results and its
capture groups std::sub_match.
std::sub_match
// subMatch.cpp
...
#include <regex>
...
using std::cout;
std::string privateAddress="192.168.178.21";
std::string regEx(R"((\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3}))");
std::regex rgx(regEx);
std::smatch smatch;
31 39 32
capture group: 21
32 31
The regular expression regEx stands for an IPv4 address. regEx is used to extract the components
of the address using capture groups. Finally, the capture groups and the characters in ASCII are
displayed in hexadecimal values.
Match
std::regex_match determines if text matches a text pattern. You can further analyse the search
result of type std::match_results.
The code snippet below shows three simple applications of std::regex_match: a C string, a C++
string and a range returning only a boolean. The three variants are available for std::match_results
objects respectively.
std::match
// match.cpp
...
#include <regex>
...
std::string numberRegEx(R"([-+]?([0-9]*\.[0-9]+|[0-9]+))");
std::regex rgx(numberRegEx);
const char* numChar{"2011"};
if (std::regex_match(numChar, rgx)){
std::cout << numChar << " is a number." << std::endl;
} // 2011 is a number.
Search
std::regex_search checks if text contains a text pattern. You can use the function with and without
a std::match_results object and apply it to a C string, a C++ string or a range.
The example below shows how to use std::regex_search with texts of type const char*,
std::string, const wchar_t* and std::wstring.
std::search
// search.cpp
...
#include <regex>
...
// const char*
std::cmatch cmatch;
// std::string
std::smatch smatch;
// const wchar_t*
std::wcmatch wcmatch;
// std::wstring
std::wsmatch wsmatch;
Replace
std::regex_replace replaces sequences in a text matching a text pattern. It returns in the simple
form std::regex_replace(text, regex, replString) its result as string. The function replaces an
occurrence of regex in text with replString.
std::replace
// replace.cpp
...
#include <regex>
...
using namespace std;
string future{"Future"};
string unofficialName{"The unofficial name of the new C++ standard is C++0x."};
regex rgxCpp{R"(C\+\+0x)"};
string newCppName{"C++11"};
Regular Expressions 173
regex rgxOff{"unofficial"};
string makeOfficial{"official"};
string officialName{regex_replace(newName, rgxOff, makeOfficial)};
In addition to the simple version, C++ has a version of std::regex_replace working on ranges. It
enables you to push the modified string directly into another string:
All variants of std::regex_replace have an additional optional parameter. If you set the param-
eter to std::regex_constants::format_no_copy you will get the part of the text matching the
regular expression, the unmatched text is not copied. If you set the parameter to std::regex_-
constants::format_first_only std::regex_replace will only be applied once.
Format
std::regex_replace and std::match_results.format in combination with capture groups enables
you to format text. You can use a format string together with a placeholder to insert the value.
Here are both possibilities:
Formatting with regex
// format.cpp
...
#include <regex>
...
std::string future{"Future"};
const std::string unofficial{"unofficial, C++0x"};
const std::string official{"official, C++11"};
std::regex regValues{"(.*),(.*)"};
std::string standardText{"The $1 name of the new C++ standard is $2."};
std::string textNow= std::regex_replace(unofficial, regValues, standardText);
Regular Expressions 174
std::smatch smatch;
if (std::regex_match(official, smatch, regValues)){
std::cout << smatch.str(); // official,C++11
std::string textFuture= smatch.format(standardText);
std::cout << textFuture << std::endl;
} // The official name of the new C++ standard is C++11.
Repeated Search
It’s quite convenient to iterate with std::regex_iterator and std::regex_token_iterator over the
matched texts. std::regex_iterator supports the matches and their capture groups. std::regex_-
token_iterator supports more. You can address the components of each capture and by using a
negative index, your can access the text between the matches.
std::regex_iterator
C++ defines the following four type synonyms for std::regex_iterator.
Regular Expressions 175
You can use std::regex_iterator to count the occurrences of the words in a text:
std::regex_iterator
// regexIterator.cpp
...
#include <regex>
...
using std::cout;
std::string text{"That's a (to me) amazingly frequent question. It may be the most f\
requently asked question. Surprisingly, C++11 feels like a new language: The pieces \
just fit together better than they used to and I find a higher-level style of progra\
mming more natural than before and as efficient as ever."};
std::regex wordReg{R"(\w+)"};
std::sregex_iterator wordItBegin(text.begin(), text.end(), wordReg);
const std::sregex_iterator wordItEnd;
std::unordered_map<std::string, std::size_t> allWords;
for (; wordItBegin != wordItEnd; ++wordItBegin){
++allWords[wordItBegin->str()];
}
for (auto wordIt: allWords) cout << "(" << wordIt.first << ":"
<< wordIt.second << ")";
// (as:2)(of:1)(level:1)(find:1)(ever:1)(and:2)(natural:1) ...
A word consists of a least one character (\w+). This regular expression is used to define the begin
iterator wordItBegin and the end iterator wordItEnd. The iteration through the matches happens in
the for loop. Each word increments the counter: ++allWords[wordItBegin]->str()]. A word with
counter equals to 1 is created if it is not already in allWords.
std::regex_token_iterator
C++ defines the following four type synonyms for std::regex_token_iterator.
Regular Expressions 176
std::regex_token_iterator enables you by using indexes to specify which capture groups you are
interested in explicitly. If you don’t specify the index, you will get all capture groups, but you can
also request specific capture groups using its respective index. The -1 index is special: You can use
-1 to address the text between the matches:
std::regex_token_iterator
// tokenIterator.cpp
...
using namespace std;
regex regBook(R"((\w+)\s(\w+),([\w\s\+]*),(\d{4}))");
sregex_token_iterator bookItBegin(text.begin(), text.end(), regBook);
regex regBookNeg(":");
sregex_token_iterator bookItNegBegin(text.begin(), text.end(), regBookNeg, -1);
bookItBegin using no indices and bookItNegbegin using the negative index returns both the total
capture group, but bookNameIssueBegin only the second and fourth capture group {{2,4}}.
14. Input and Output Streams
The input and output streams1 enable you to communicate with the outside world. A stream is an
infinite character stream on which you can push or pull data. Push is called writing, pull is called
reading.
The input and output streams
• were used long before the first C++ standard (C++98) in 1998,
• are a for the extensibility designed framework,
• are implemented according to the object-oriented and generic paradigms.
Hierarchy
basic_streambuf<>
Reads and writes the data.
ios_base
Properties of all stream classes independent on the character type.
1 http://en.cppreference.com/w/cpp/header/iostream
Input and Output Streams 179
basic_ios<>
Properties of all stream classes dependent of the character type.
basic_istream<>
Base for the stream classes for the reading of the data.
basic_ostream<>
Base for the stream classes for the writing of the data.
basic_iostream<>
Base for the stream classes for the reading and writing of the data.
The class hierarchy has type synonyms for the character types char and wchar_t. Names not starting
with w are type synonyms for char, names starting with w for wchar_t.
The base classes of the class std::basic_iostream<> are virtually derived from std::basic_ios<>,
therefore std::basic_iostream<> has only one instance of std::basic_ios.
C++ has four predefined stream objects for the convenient dealing with the keyboard and the
monitor.
The stream objects are sufficient to write a program that reads from the command line and returns
the sum.
The stream objects
// IOStreams.cpp
...
#include <iostream>
int main(){
std::cout << "Type in your numbers";
std::cout << "(Quit with an arbitrary character): " << std::endl;
// 2000 <Enter> 11 <a>
int sum{0};
int val;
while (std::cin >> val) sum += val;
std::cout << "Sum: " << sum; // Sum: 2011
}
The small program above uses the stream operators << and >> and the stream manipulator std::endl.
The insert operator << pushes characters onto the output stream std::cout; the extract operator
>> pulls the characters from the input stream std::cin. You can build chains of insert or extract
operators because both operators return a reference to themselves.
std::endl is a stream manipulator because it puts a ‘\n’ character onto std::cout and flushes the
output buffer.
Here are the most frequently used stream manipulators.
Input
You can read in C++ in two way from the input stream: Formatted with the extractor >> and
unformatted with explicit methods.
Input and Output Streams 181
Formatted Input
Unformatted Input
There are many methods for the unformatted input from an input stream is.
Method Description
is.get(ch) Reads one character into ch.
is.get(buf, num) Reads at most num characters into the buffer buf.
is.getline(buf, num[, delim]) Reads at most num characters into the buffer buf.
Uses optionally the line-delimiter delim (default \n).
is.gcount() Returns the number of characters that were last
extracted from is by an unformatted operation.
is.ignore(streamsize sz= 1, int delim= Ignores sz characters until delim.
end-of-file)
is.peek() Gets one characters from is without consuming it.
is.unget() Pushes the last read character back to is.
is.putback(ch) Pushes the character ch onto the stream is.
Input and Output Streams 182
// inputUnformatted.cpp
...
#include <iostream>
...
std::string line;
std::cout << "Write a line: " << std::endl;
Output
You can push characters with the insert operator << onto the output stream.
The insert operator <<
Format Specifier
Format specifiers enable you to adjust the input and output data explicitly.
Input and Output Streams 183
std::cout.setf(std::ios::hex, std::ios::basefield);
std::cout << num << std::endl; // 7db
std::cout.setf(std::ios::dec, std::ios::basefield);
std::cout << num << std::endl; // 2011
The followings tables present the important format specifiers. The format specifiers are sticky except
for the field width, which is reset after each application.
The manipulators without any arguments require the header <iostream>, and the manipulators with
arguments require the header <iomanip>.
Format specifier
// formatSpecifierOutput.cpp
...
#include <iomanip>
#include <iostream>
...
std::cout.fill('#');
std::cout << -12345;
std::cout << std::setw(10) << -12345; // ####-12345
std::cout << std::setw(10) << std::left << -12345; // -12345####
std::cout << std::setw(10) << std::right << -12345; // ####-12345
std::cout << std::setw(10) << std::internal << -12345; //-####12345
Streams
A stream is an infinite data stream on which you can push or pull data. String streams and file
streams enable strings and files to interact with the stream directly.
String Streams
String streams need the header <sstream>. They are not connected to an input or output stream and
store their data in a string.
Whether you use a string stream for input or output or with the character type char or wchar_t
there are various string stream classes:
std::stringstream os;
os << "New String";
os.str("Another new String");
std::string os;
std::string str;
os >> str;
str= os.str();
std::stringstream os;
os.str("");
String streams are often used for the type safe conversion between strings and numeric values:
String streams
// stringStreams.cpp
...
#include <sstream>
...
File Streams
File streams enable you to work with files. They need the header <fstream>. The file streams
automatically manage the lifetime of their file.
Whether you use a file stream for input or output or with the character type char or wchar_t there
are various file stream classes:
Flag Description
std::ios::in Opens the file stream for reading (default for std::ifstream and std::wifstream).
std::ios::out Opens the file stream for writing (default for std::ofstream and std::wofstream).
std::ios::app Appends the character to the end of the file stream.
std::ios::ate Sets the initial position of the file position pointer on the end of the file stream.
std::ios::trunc Deletes the original file.
std::ios::binary Suppresses the interpretation of an escape sequence in the file stream.
It’s quite easy to copy the file named in to the file named out with the file buffer in.rdbuf(). The
error handling is missing in this short example.
Input and Output Streams 189
#include <fstream>
...
std::ifstream in("inFile.txt");
std::ofstream out("outFile.txt");
out << in.rdbuf();
If you combine the C++ flags, you can compare the C++ and C modes to open a file.
The file has to exist with the mode "r" and "r+". In contrary, the file is be created with "a" and
"w+". The file is overwritten with "w".
Flag Description
infile.open(name) Opens the file name for reading.
infile.open(name, flags) Opens the file name with the flags flags for reading.
infile.close() Closes the file name.
infile.is_open() Checks if the file is open.
Random Access
Random access enables you to set the file position pointer arbitrarily.
When a file stream is constructed, the files position pointer points to the beginning of the file. You
can adjust the position with the methods of the file stream file.
Input and Output Streams 190
Method Description
file.tellg() Returns the read position of file.
file.tellp() Returns the write position of file.
file.seekg(pos) Sets the read position of file to pos.
file.seekp(pos) Sets the write position of file to pos.
file.seekg(off, rpos) Sets the read position of file to the offset off relative to rpos.
file.seekp(off, rpos) Sets the write position of file to the offset off relative to rpos.
std::ios::beg
Position at the beginning of the file.
std::ios::cur
Position at the current position.
std::ios::end
Position at the end of the file.
Random access
// randomAccess.cpp
...
#include <fstream>
...
std::string random{"random.txt"};
writeFile(random);
std::ifstream inFile(random);
if (!inFile){
std::cerr << "Could not open file " << random << std::endl;
exit(1);
}
std::string line;
inFile.seekg(20, std::ios::cur);
getline(inFile, line);
std::cout << line; // 2 0123456789
inFile.seekg(-20, std::ios::end);
getline(inFile, line);
std::cout << line; // 9 0123456789
State of a stream
Here are examples for conditions causing the different states of a stream:
std::ios::eofbit
std::ios::failbit
std::ios::badbit
stream.clear()
Initializes the flags and sets the stream in the goodbit state.
stream.clear(sta)
Initializes the flags and set the stream into sta state.
stream.rdstate()
Returns the current state.
stream.setstate(fla)
Sets the additional flag fla.
Operations on a stream only work if the stream is in the goodbit state. If the stream is in the badbit
state you cannot set it to goodbit state.
Input and Output Streams 193
State of a stream
// streamState.cpp
...
#include <iostream>
...
std::cout << std::cin.fail() << std::endl; // false
int myInt;
std::cin.clear();
std::cout << std::cin.fail() << std::endl; // false
The input of the character a causes the stream std::cin to be in std::ios::failbit state. Therefore
a and std::cin.fail() cannot be displayed. At first you have to initialize the stream std::cin.
For overloading the input and output operators you have to keep a few rules in mind:
• To support the chaining of input and output operations you have to get and return the input
and output streams by non-constant reference.
• To access the private members of the class, the input and output operators have to be friends
of your data type.
• The input operator >> takes its data type as a non-constant reference.
• The output operator << takes its data type as a constant reference.
Input and Output Streams 194
// overloadingInOutput.cpp
class Fraction{
public:
Fraction(int num= 0, int denom= 0):numerator(num), denominator(denom){}
friend std::istream& operator>> (std::istream& in, Fraction& frac);
friend std::ostream& operator<< (std::ostream& out, const Fraction& frac);
private:
int numerator;
int denominator;
};
Using cppreference.com
At the time of the writing this book (October 2017), I had no C++-compiler at my disposal
that supports the new filesystem library; therefore, I executed the programs in this chapter
with the newest GCC-compiler on cppreference.com: filesystem3 that supports the new
filesystem library. To use the new filesystem library you may have to include and apply
the experimental namespace.
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
All examples in this chapter are written without the experimental namespace.
The library is based on the three concepts: file, file name and path.
• A file is an object that holds data such that you can write to it or read from it. A file has a name
and a file type. A file type can be a directory, hard link, symbolic link or a regular file.
– A directory is a container for holding other files. The current directory is represented by
a dot "."; the parent directory is represented by two dots "..".
– A hard link associates a name with an existing file.
– A symbolic link associates a name with a path that may exist.
– A regular file is a directory entry which is neither a directory, a hard link, nor a symbolic
link.
• A file name is a string that represents a file. It is implementation-defined which characters are
allowed, how long the name could be or if the name is case sensitive.
• A path is a sequence of entries that identifies the location for a file. It has an optional root-name
such a “C:” on Windows, followed by a root-directory such a “/” on Unix. Additional parts can
be directories, hard links, symbolic links, or regular files. A path can be absolute, canonical, or
relative.
1 http://en.cppreference.com/w/cpp/filesystem
2 http://www.boost.org/doc/libs/1_65_1/libs/filesystem/doc/index.htm
3 http://en.cppreference.com/w/cpp/filesystem
Filesystem library 196
// filesystem.cpp
...
#include <filesystem>
...
namespace fs = std::filesystem;
std::cout << "Current path: " << fs::current_path() << std::endl; // (1)
std::ofstream("sandbox/file1.txt");
fs::path symPath= fs::current_path() /= "sandbox"; // (3)
symPath /= "syma";
fs::create_symlink("a", "symPath"); // (4)
fs::current_path() (1) returns the current path. You can create a directory hierarchy (2) with
std::filesystem::create_directories. The /= operator is overloaded for a path (3). Therefore,
I can directly create a symbolic link (4) or check the properties of a file. The call recursive_-
directory_iterator (5) allows you to traverse directories recursively.
Filesystem library 197
Classes
There are many classes encapsulating a specific aspect of the filesystem.
Class Description
path Represents a path.
filesystem_error Defines an exception object.
directory_entry Represents a directory entry.
directory_iterator Defines a directory iterator.
recursive_directory_iterator Defines a recursive directory iterator.
file_status Stores information about the file.
space_info Represents filesystem information.
file_type Indicates the type of a file.
perms Represents file access permissions.
perm_options Represents options for the function permissions.
copy_options Represents options for the functions copy and
copy_file.
directory_options Represents options for the functions
directory_iterator and
recursive_directory_iterator.
file_time_type Represents file time.
Filesystem library 198
// perms.cpp
...
#include <filesystem>
...
namespace fs = std::filesystem;
...
std::ofstream("rainer.txt");
printPerms(fs::status("rainer.txt").permissions());
Non-member functions
Many non-member functions exist for manipulating the filesystem.
// fileTime.cpp
...
#include <filesystem>
...
namespace fs = std::filesystem;
using namespace std::chrono_literals;
...
std::ofstream(path.c_str());
auto ftime = fs::last_write_time(path); // (1)
cftime = std::chrono::system_clock::to_time_t(ftime);
std::cout << "Local time on client "
<< std::asctime(std::localtime(&cftime)) << std::endl;
Line (1) gives the write time of the newly created file. You can use ftime in (2) to initialise
std::chrono::system_clock. ftime is of type std::filesystem::file_time_type which is in this
case an alias for std::chrono::system_clock; therefore, you can initialise std::localtime in (3)
and present the calendar time in a textual representation. If you use std::gmtime (4) instead of
std::localtime, nothing will change. This puzzled me because the Coordinated Universal Time
(UTC) differs 2 hours from the local time in Germany. That’s due to the server for the online-
compiler on en.cppreference.com. UTS and local time are set to the same time on the server.
Here is the output of the program. I moved the write time of the file 2 hours to the future (5) and read
it back from the filesystem (6). This adjusts the time so it corresponds to the local time in Germany.
// space.cpp
...
#include <filesystem>
...
namespace fs = std::filesystem;
...
File types
By using the following predicates, you can easily ask for the type of file.
Filesystem library 203
...
namespace fs = std::filesystem;
...
fs::create_directory("rainer");
printStatus("rainer");
std::ofstream("rainer/regularFile.txt");
printStatus("rainer/regularFile.txt");
fs::create_directory("rainer/directory");
printStatus("rainer/directory");
mkfifo("rainer/namedPipe", 0644);
printStatus("rainer/namedPipe");
fs::create_symlink("rainer/regularFile.txt", "symlink");
printStatus("symlink");
printStatus("dummy.txt");
fs::remove_all("rainer");
Filesystem library 205
16. Multithreading
For the first time with C++11, C++ supports native multithreading. This support consists of two
parts: A well-defined memory model and a standardised threading interface.
Memory Model
The foundation of multithreading is a well-defined memory model. This memory model has to deal
with the following points:
The C++ memory model has a lot in common with its predecessor: the Java memory model. On
the contrary, C++ permits the breaking of sequential consistency. The sequential consistency is the
default behaviour of atomic operations.
The sequential consistency provides two guarantees.
• The copy assignment operator for MyType, for all base classes of MyType and all non-static
members of MyType, must be trivial. Only a compiler generated copy assignment operator is
trivial.
• MyType must not have virtual methods or base classes.
• MyType must be bitwise copyable and comparable so that the C functions memcpy or memcmp can
be applied.
Atomic data types have atomic operations. For example load and store:
Multithreading 207
std::atomic_int x, y;
int r1, r2;
void writeX(){
x.store(1);
r1= y.load();
}
void writeY(){
y.store(1);
r2= x.load();
}
x= 0;
y= 0;
std::thread a(writeX);
std::thread b(writeY);
a.join();
b.join();
Threads
To use the multithreading interface of C++ you need the header <thread>.
Creation
A thread std::thread represents an executable unit. This executable unit, which the thread
immediately starts, gets its work package as a callable unit. A callable unit can be a function, a
function object or a lambda function:
Multithreading 208
Thread creation
// threadCreate.cpp
...
#include <thread>
...
using namespace std;
void helloFunction(){
cout << "function" << endl;
}
class HelloFunctionObject {
public:
void operator()() const {
cout << "function object" << endl;
}
};
HelloFunctionObject helloFunctionObject;
thread t2(helloFunctionObject); // function object
Lifetime
The creator of a thread has to take care of the lifetime of its created thread. The executable unit of
the created thread ends with the end of the callable. Either the creator is waiting until the created
thread t is done (t.join()) or the creator detaches itself from the created thread: t.detach(). A
thread t is joinable if no operation t.join() or t.detach() was performed on it. A joinable thread
calls in its destructor the exception std::terminate, and the program terminates.
Multithreading 209
Lifetime of a thread
// threadLifetime.cpp
...
#include <thread>
...
HelloFunctionObject helloFunctionObject;
thread t2(helloFunctionObject); // function object
t1.join();
t2.join();
t3.join();
A thread that is detached from its creator is typically called a daemon thread because it runs in the
background.
#include <thread>
...
std::thread t([]{ cout << "lambda function"; });
std::thread t2;
t2= std::move(t);
By performing t2= std::move(t) thread t2 has the callable of thread t. Assuming thread
t2 already had a callable and is joinable the C++ runtime would call std::terminate.
This happens exactly in t2= std::move(t3) because t2 neither executed t2.join() nor
t2.detach() before.
Arguments
A std::thread is a variadic template. This means in particular that it can get an arbitrary number
of arguments by copy or by reference. Either the callable or the thread can get the arguments. The
thread delegates them to the callable: tPerCopy2 and tPerReference2.
Multithreading 210
#include <thread>
...
string s{"C++"};
The first two threads get their argument s by copy, the second two by reference.
Multithreading 211
using std::this_thread::sleep_for;
using std::this_thread::get_id;
struct Sleeper{
Sleeper(int& i_):i{i_}{};
void operator() (int k){
for (unsigned int j= 0; j <= 5; ++j){
sleep_for(std::chrono::milliseconds(100));
i += k;
}
std::cout << get_id(); // undefined behaviour
}
private:
int& i;
};
This program snippet has two pieces of undefined behaviour. First the lifetime of std::cout
is bound to the lifetime of the main thread. Second the created thread gets its variable
valSleeper by reference. The issue is that the created thread lives longer than its creator,
therefore std::cout and valSleeper lose their validity if the main thread is done.
Operations
You can perform many operations on a thread t.
Multithreading 212
Method Description
t.join() Waits until thread t has finished its executable unit.
t.detach() Executes the created thread t independent of the creator.
t.joinable() Checks if thread t supports the calls join or detach.
t.get_id() and Returns the identity of the thread.
std::this_thread::get_id()
You can only call t.join() or t.detach() once on a thread t. If you attempt to call these more than
once you get the exception std::system_error. std::thread::hardware_concurrency returns the
number of cores or 0 if the runtime cannot determine the number. The sleep_until and sleep_for
operations needs a time point or a time duration as argument.
Threads cannot be copied but can be moved. A swap operation performs a move when possible.
Operations on a thread
// threadMethods.cpp
...
#include <thread>
...
using std::this_thread::get_id;
std::thread::hardware_concurrency(); // 4
t1.swap(t2);
t1.get_id(); // 139783030257408
Multithreading 213
t2.get_id(); // 139783038650112
get_id(); // 140159896602432
Shared Variables
If more than one thread is sharing a variable, you have to coordinate the access. That’s the job for
mutexes and locks in C++.
Data race
A data race
A data race is a state in which at least two threads access a shared data at the same time, and
at least one of the threads is a writer. Therefore the program has undefined behaviour.
You can observe very well the interleaving of threads if a few threads write to std::cout. The output
stream std::cout is, in this case, the shared variable.
Unsychronised writing to std::cout
// withoutMutex.cpp
...
#include <thread>
...
struct Worker{
Worker(string n):name(n){};
void operator() (){
for (int i= 1; i <= 3; ++i){
this_thread::sleep_for(chrono::milliseconds(200));
cout << name << ": " << "Work " << i << endl;
}
}
private:
string name;
};
std::cout is in the example the shared variable, which should have exclusive access to the stream.
Mutexes
Mutex (mutual exclusion) m guarantees that only one thread can access the critical region at one
time. They need the header <mutex>. A mutex m locks the critical section by the call m.lock() and
unlocks it by m.unlock().
Synchronisation with std::mutex
// mutex.cpp
...
#include <mutex>
#include <thread>
...
std::mutex mutexCout;
Multithreading 215
struct Worker{
Worker(string n):name(n){};
void operator() (){
for (int i= 1; i <= 3; ++i){
this_thread::sleep_for(chrono::milliseconds(200));
mutexCout.lock();
cout << name << ": " << "Work " << i << endl;
mutexCout.unlock();
}
}
private:
string name;
};
Now each thread after each other writes coordinated to std::cout because it uses the same mutex
mutexCout.
C++ has five different mutexes. They can lock recursively, tentative with and without time
constraints.
Multithreading 216
Mutex variations
Deadlocks
Deadlocks
A deadlock is a state in which two or more threads are blocked because each thread waits for
the release of a resource before it releases its resource.
You can get a deadlock very quickly if you forget to call m.unlock(). That happens for example in
case of an exception in the function getVar().
m.lock();
sharedVar= getVar();
m.unlock()
Locking two mutexes in the wrong order is another typical reason for a deadlock.
Multithreading 217
A deadlock
// deadlock.cpp
...
#include <mutex>
...
struct CriticalData{
std::mutex mut;
};
CriticalData c1;
CriticalData c2;
Locks
You should encapsulate a mutex in a lock to release the mutex automatically. A lock is an
implementation of the RAII idiom because the lock binds the lifetime of the mutex to its lifetime.
C++11 has std::lock_guard for the simple and std::unique_lock for the advanced use case,
respectively. Both need the header <mutex>. With C++14 C++ has a std::shared_lock which is
in the combination with the mutex std::shared_time_mutex the base for reader-writer locks.
std::lock_guard
std::lock_guard supports only the simple use case. Therefore it can only bind its mutex in the
constructor and release it in the destructor. So the synchronisation of the worker example is reduced
to the call of the constructor.
Synchronisation with std::lock_guard
// lockGuard.cpp
...
std::mutex coutMutex;
struct Worker{
Worker(std::string n):name(n){};
void operator() (){
for (int i= 1; i <= 3; ++i){
std::this_thread::sleep_for(std::chrono::milliseconds(200));
std::lock_guard<std::mutex> myLock(coutMutex);
std::cout << name << ": " << "Work " << i << std::endl;
}
}
private:
std::string name;
};
std::unique_lock
The usage of std::unique_lock is more expensive than the usage of std::lock_guard. In contrary
a std::unique_lock can be created with and without mutex, can explicitly lock or release its mutex
or can delay the lock of its mutex.
The following table shows the methods of a std::unique_lock lk.
Multithreading 219
Method Description
lk.lock() Locks the associated mutex.
std::lock(lk1, lk2, ...) Locks atomically the arbitrary number of
associated mutexes.
lk.try_lock() and Tries to lock the associated mutex.
lk.try_lock_for(relTime) and
lk.try_lock_until(absTime)
Deadlocks caused by acquiring locks in different order can easily be solved by std::atomic.
std::unique_lock
// deadLockResolved. cpp
...
#include <mutex>
...
CriticalData c1;
CriticalData c2;
Multithreading 220
Because of the argument std::defer_lock of the std::unique_lock, the locking of a.mut and b.mut
is deferred. The locking takes place atomically in the call std::lock(guard1, guard2).
std::shared_lock
#include <mutex>
...
std::shared_timed_mutex sharedMutex;
std::unique_lock<std::shared_timed_mutex> writerLock(sharedMutex);
std::shared_lock<std::shared_time_mutex> readerLock(sharedMutex);
std::shared_lock<std::shared_time_mutex> readerLock2(sharedMutex);
The example presents the typical reader-writer lock scenario. The writerLock of type std::unique_-
lock<std::shared_timed_mutex> can only exclusively have the sharedMutex. Both of the reader
locks readerLock and readerLock2 of type std::shared_lock<std::shared_time_mutex> can share
the same mutex sharedMutex.
Thread-safe Initialization
If you don’t modify the data, it’s sufficient to initialise them in a thread-safe way. C++ offers various
ways to achieve this: using a constant expression, using static variables with block scope and using
the function std::call_once in combination with the flag std::once::flag.
Multithreading 221
Constant Expressions
A constant expression is initialised at compile time. Therefore they are per se thread-safe. By using
the keyword constexpr before a variable, the variable becomes a constant expression. Instances of
user-defined type can also be a constant expression and therefore be initialised in a thread-safe way
if the methods are declared as constant expressions.
struct MyDouble{
constexpr MyDouble(double v):val(v){};
constexpr double getValue(){ return val; }
private:
double val
};
If you define a static variable in a block, the C++11 runtime guarantees that the variable is initialised
in a thread-safe way.
void blockScope(){
static int MySharedDataInt= 2011;
}
std::call_once takes two arguments: the flag std::once_flag and a callable. The C++ runtime
guarantees with the help of the flag std::once_flag that the callable is executed exactly once.
Thread-safe initialisation
// callOnce.cpp
...
#include <mutex>
...
once_flag onceFlag;
void do_once(){
Multithreading 222
Although both threads executed the function do_once only one of them is successful, and the lambda
function []{cout << "Only once." << endl;} is executed exactly once.
You can further use the same std::once_flag to register different callables and only one of this
callables is called.
Each thread has its copy of the thread_local string, therefore, each string s modifies its string
independently, and each string has its unique address:
Condition Variables
Condition variables enable threads to be synchronised via messages. They need the header <condition_-
variable>. One thread acts as a sender, and the other as a receiver of the message. The receiver waits
for the notification of the sender. Typical use cases for condition variables are producer-consumer
workflows.
A condition variable can be the sender but also the receiver of the message.
Method Description
cv.notify_one() Notifies a waiting thread.
cv.notify_all() Notifies all waiting threads.
cv.wait(lock, ...) Waits for the notification while holding a std::unique_lock.
cv.wait_for(lock, relTime, ...) Waits for a time duration for the notification while holding a
std::unique_lock.
cv.wait_until(lock, absTime, ...) Waits until a time for the notification while holding a
std::unique_lock.
Sender and receiver need a lock. In case of the sender a std::lock_guard is sufficient, because it
only once calls lock and unlock. In the case of the receiver a std::unique_lock is necessary, because
it typically often locks and unlocks its mutex.
Multithreading 224
Condition variable
// conditionVariable.cpp
...
#include <condition_variable>
...
std::mutex mutex_;
std::condition_variable condVar;
bool dataReady= false;
void doTheWork(){
std::cout << "Processing shared data." << std::endl;
}
void waitingForWork(){
std::cout << "Worker: Waiting for work." << std::endl;
std::unique_lock<std::mutex> lck(mutex_);
condVar.wait(lck, []{ return dataReady; });
doTheWork();
std::cout << "Work done." << std::endl;
}
void setDataReady(){
std::lock_guard<std::mutex> lck(mutex_);
dataReady=true;
std::cout << "Sender: Data is ready." << std::endl;
condVar.notify_one();
}
std::thread t1(waitingForWork);
std::thread t2(setDataReady);
Using a condition variable may sound easy but there a two critical issues.
Multithreading 225
Tasks
In addition to threads, C++ has tasks to perform work asynchronously. Tasks need the header
<future>. A task is parameterised with a work package and consists of the two associated
components, a promise and a future. Both are connected via a data channel. The promise executes
the work packages and puts the result in the data channel; the associated future picks up the result.
Both communication endpoints can run in separate threads. What’s special is that the future can pick
up the result at a later time. Therefore the calculation of the result by the promise is independent of
the query of the result by the associated future.
Multithreading 226
#include <future>
#include <thread>
...
int res;
std::thread t([&]{ res= 2000+11;});
t.join();
std::cout << res << std::endl; // 2011
The child thread t and the asynchronous function call std::async calculates the sum of 2000 and
11. The creator thread gets the result from its child thread t via the shared variable res. The call
std::async creates the data channel between the sender (promise) and the receiver (future). The
future asks the data channel with fut.get() for the result of the calculation. The fut.get call is
blocking.
std::async
std::async behaves like an asynchronous function call. This function call takes a callable and
its arguments. std::async is a variadic template and can, therefore, take an arbitrary number of
arguments. The call of std::async returns a future object fut. That’s your handle for getting the
result via fut.get(). Optionally you can specify a start policy for std::async. You can explicitly
determine with the start policy if the asynchronous call should be executed in the same thread
(std::launch::deferred) or in another thread (std::launch::async).
What’s special about the call auto fut= std::async(std::launch::deferred, ... ) is that the
promise will not immediately be executed. The call fut.get() lazy starts the promise.
Lazy and eager with std::async
// asyncLazyEager.cpp
...
#include <future>
...
using std::chrono::duration;
using std::chrono::system_clock;
using std::launch;
std::this_thread::sleep_for(std::chrono::seconds(1));
The output of the program shows that the promise associated with the future asyncLazy is executed
one second later than the promise associated with the future asyncEager. One second is exactly the
time duration the creator is sleeping before the future asyncLazy asks for its result.
std::packaged_task
std::packaged_task enables you to build a simple wrapper for a callable, which can later be
executed on a separate thread.
Therefore four steps are necessary.
I. Wrap your work:
sumTask(2000, 11);
sumResult.get();
You can move either the std::package_task or the std::future in a separate thread.
std::packaged_task
// packaged_task.cpp
...
#include <future>
...
struct SumUp{
int operator()(int beg, int end){
for (int i= beg; i < end; ++i ) sum += i;
return sum;
}
private:
int beg;
int end;
int sum{0};
};
int begin{1};
int increment{5000};
int end= begin + increment;
begin= end;
end += increment;
sumThread.detach();
}
The promises (std::packaged_task) are moved into the std::deque allTasks. The program iterates
in the while loop through all promises. Each promise runs in its thread and performs its addition in
the background (sumThread.detach()). The result is the sum of all numbers from 0 to 100000.
Method Description
prom.swap(prom2) and Swaps the promises.
std::swap(prom, prom2)
If the promise sets the value or the exception more then once a std::future_error exception is
thrown.
Method Description
fut.share() Returns a std::shared_future.
fut.get() Returns the result which can be a value or an exception.
fut.valid() Checks if the result is available. Returns after the call fut.get() false.
fut.wait() Waits for the result.
fut.wait_for(relTime) Waits for a time duration for the result.
fut.wait_until(absTime) Waits until a time for the result.
Multithreading 231
If a future fut asks more than once for the result, a std::future_error exception is thrown. The
future creates a shared future by the call fut.share(). Shared future are associated with their
promise and can independently ask for the result. A shared future has the same interface as a future.
Here is the usage of promise and future.
Promise and future
// promiseFuture.cpp
...
#include <future>
...
int a= 20;
int b= 10;
std::promise<int> prodPromise;
std::future<int> prodResult= prodPromise.get_future();
The promise prodPromise is moved into a separate thread and performs its calculation. The future
gets the result by prodResult.get().
Multithreading 232
void doTheWork(){
std::cout << "Processing shared data." << std::endl;
}
std::promise<void> sendReady;
auto fut= sendReady.get_future();
The call of the promise prom.set_value() wakes up the future which then can
perform its work.
Index
A bad_optional_access
abolute path bad_variante_access::variant
abs badbit::ios
absolute basic_ios<>
accumulate basic_iostream<>
aconsh basic_istream<>
acos basic_ostream<>
adjacent_difference basic_streambuf
adjacent_find before_begin::forward_list
Algorithms::Standard Template Libary beg::ios
all_of begin::container
any begin::unordered associative container
any_cast biderectional iterator
any_of binary::ios
app::ios binary_search
append::string bind
arguments::thread bit_and
array bit_or
asin bit_xor
asinh boolalpha
assign::container C
assign::string c_str::string
assign::vector call_once
async::launch callable unit
async callable unit
at::string callable
atan2 canonical path
atan canonical
atanh capacity::vector
ate::ios cbegin::container
atomic data types cbegin::unordered associative container
auto_ptr ceil
B cend::container
back::queue cend::unordered associative container
back::string cerr
back_inserter::insert iterator cin
bad classes::filesytem
bad_any_cast clear::string
Index 234
clear::vector dec
clear default constructor::container
clock default_random_engine
clog defaultfloat
close deferred::launch
compare::container deque
comparison sorted associative container detach
complexity directory
condition variable directory_entry
constant expression directory_iterator
constructor::string directory_options
constructor::string_view div
copy constructor::container divides
copy::string E
copy emplace::any
copy emplace::associative container
copy_backward emplace::optional
copy_file emplace::variant
copy_if emplace::vector
copy_n emplace_after::forward_list
copy_options emplace_back::vector
copy_symlink emplace_front::forward_list
cos empty::container
cosh enable_shared_from_this
count::sorted associative container end::container
count end::ios
count_if end::unordered associative container
cout endl
crbegin::container eof
create_directories eofbit::ios
create_directory equal
create_hard_link equal_range::sorted associative container
create_symlink equal_range
cref equal_to
cregex_iterator equivalent
cregex_token_iterator erase-remove idiom
crend::container erase::associative container
cur::ios erase::string
current_path erase::vector
cyclic references erase_after::forward_list
D exclusive_scan
data race exists
data::string exp
deadlock expired::weak_ptr
Index 235
F function object
fabs function
fail function
failbit::ios future
FIFO G
file streams gcount
file types generate
file generate_n
file_size get::shared_ptr
file_status get::unique_ptr
file_time_type get::variant
file_type get
filebuf get
filesystem_error get_deleter::shared_ptr
fill get_deleter::unique_ptr
fill_n get_future
find::sorted associative container get_id::this_thread
find::string get_id
find get_if::variant
find_first_not_of::string getline
find_first_of::string getline
find_first_of good
find_if goodbit::ios
find_if_not greater
find_last_not_of::string greater_equal
find_last_of::string H
findend hard link
floor hard_link_count
flush hardware_concurrency::thread
fmod has_value::any
for_each has_value::optional
format specifier hex
format::regex hexfloat
formatted input::streams hierachy::input and output streams
formatted output::streams high_resolution_clock
forward iterator holds_alternative::variant
forward I
forward_list ifstream
forwarding reference ignore
frexp ignore
front::queue in::ios
front::string includes
front_inserter::insert::iterator inclusive_scan
fstream index::variant
Index 236
inner_product ldexp
inplace_merge ldiv
input and output streams left
input functions::streams less
input iterator less_equal
input output functions::streams lexicographical_compare
insert iterator lifetime::thread
insert::associative container LIFO
insert::string list
insert::vector llabs
insert_after::forward_list lldiv
inserter::insert iterator lock::weak_ptr
internal lock
ios_base lock_guard
iota log10
is_bock_file log
is_character_file logical_and
is_directory logical_not
is_empty logical_or
is_fifo lower_bound::sorted associative container
is_heap lower_bound
is_heap_until lowercase
is_open M
is_other make_any
is_partitioned make_heap
is_regular_file make_optional
is_socket make_pair
is_sorted make_shared
is_sorted_until make_tuple
is_symlink make_unique
istream match::regex
istringstream match_results::regex
iterator categories max
iterator max_element
J memory model
join merge::forward_list
joinable merge::list
K merge
key::ordered associative container Mersenne Twister
key::unordered associative container min
L min_element
labs minmax
lambda function minmax
last_write_time minus
Index 237
mismatch partial_sort
modf partial_sort_copy
modulus partial_sum
move constructor::container partition
move partition_copy
multimap partition_point
multiplies path name
multiset path::classes
multithreading path
mutex peek
mutex performance associative container
N perm_options
negate permissions
next_permutation perms
noboolalpha plus
Non-member functions::filesystem pop::priority_queue
Non-modifying operations::string_view pop::queue
none_of pop::stack
noshowbase pop_back::string
noshowpos pop_back::vector
not_equal_to pop_heap
notify_all pow
notify_one predicate
nth_element predicate
numeric functions from C prev_permutation
numeric promise
O proximate
oct push::priority_queue
ofstream push::queue
once_flag push::stack
open push_back::string
operations::thread push_back::vector
optional push_heap
ostream putback
ostringstream R
out::ios RAII
output functions::streams rand
output iterator random access iterator
owns_lock random access::filestreams
P random umber distribution
packaged_task random umber generator
pair random umber
par::execution random_device
par_unseq::execution random_shuffle
Index 238
try_lock_for wait_for
try_lock_until wait_until
tuple wait_until
type traits wcerr
type::any wcin
U wclog
u16string wcout
u16string_view wcregex_iterator
u32string wcregex_token_iterator
u32string_view weak_ptr
unget weakly_canonical
unique::forward_list wfilebuf
unique::list wfstream
unique::shared_ptr wifstream
unique wistringstream
unique_copy wofstream
unique_lock wostringstream
unique_ptr ws
universal reference wsregex_iterator
unordered_map wsregex_token_iterator
unordered_multimap wstring
unordered_multiset wstring_view
unordered_set wstringstream
upper_bound::sorted associative container Y
upper_bound yield::this_thread
uppercase
use_count::shared_ptr
use_count::weak_ptr
user defined data types
utility
V
valid
value::optional
value::ordered associative container
value::unordered associative container
value_by_exception::variant
value_or::optional
variant
vector
visit
W
wait
wait
wait_for