Book Image

C++20 STL Cookbook

By : Bill Weinman
Book Image

C++20 STL Cookbook

By: Bill Weinman

Overview of this book

Fast, efficient, and flexible, the C++ programming language has come a long way and is used in every area of the industry to solve many problems. The latest version C++20 will see programmers change the way they code as it brings a whole array of features enabling the quick deployment of applications. This book will get you up and running with using the STL in the best way possible. Beginning with new language features in C++20, this book will help you understand the language's mechanics and library features and offer insights into how they work. Unlike other books, the C++20 STL Cookbook takes an implementation-specific, problem-solution approach that will help you overcome hurdles quickly. You'll learn core STL concepts, such as containers, algorithms, utility classes, lambda expressions, iterators, and more, while working on real-world recipes. This book is a reference guide for using the C++ STL with its latest capabilities and exploring the cutting-edge features in functional programming and lambda expressions. By the end of the book C++20 book, you'll be able to leverage the latest C++ features and save time and effort while solving tasks elegantly using the STL.
Table of Contents (13 chapters)

About this book

The C++20 STL Cookbook provides recipes to help you get the most out of the C++ STL (Standard Template Library), including new features introduced with C++20.

C++ is a rich and powerful language. Built upon C, with syntactic extensions for type safety, generic programming, and object-oriented programming, C++ is essentially a low-level language. The STL provides a broad set of higher-level classes, functions, and algorithms to make your programming job easier, more effective, and less prone to error.

I've often said that C++ is five languages cobbled into one. The formal specification includes 1) the entire C language, 2) C's cryptic-yet-powerful macro preprocessor, 3) a feature-rich class/object model, 4) a generic programming model called templates, and finally, built upon C++ classes and templates, 5) the STL.

Prerequisite knowledge

This book presumes that you have a basic understanding of C++, including syntax, structure, data types, classes and objects, templates, and the STL.

The recipes and examples in this book presume that you understand the need to #include certain headers to use library functions. The recipes don't usually list all the necessary headers, preferring to focus on the techniques at hand. You're encouraged to download the example code, which has all the necessary #include directives and other front matter.

You may download the example code from GitHub: https://github.com/PacktPublishing/CPP-20-STL-Cookbook.

These assumptions mean that when you see a piece of code like this:

cout << "hello, world\n";

You should already know that you'll need to put this code in a main() function, you'll need to #include the <iostream> header, and cout is an object in the std:: namespace:

#include <iostream>
int main() {
    std::cout << "hello, world\n";
}

The STL's power is derived from templates (a brief primer)

Templates are how C++ does generic programming, code that's independent of type while retaining type safety. C++ templates allow you to use tokens as placeholders for types and classes, like this:

template<typename T>
T add_em_up(T& lhs, T& rhs) {
    return lhs + rhs;
}

A template may be used for classes and/or functions. In this template function, the T represents a generic type, which allows this code to be used in the context of any compatible class or type:

int a{ 72 };  // see braced initialization below
int b{ 47 };
cout << add_em_up<int>(a, b) << "\n";

This invokes the template function with an int type. This same code can be used with any type or class that supports the + operator.

When the compiler sees a template invocation, like add_em_up<int>(a, b), it creates a specialization. This is what makes the code type safe. When you invoke add_em_up() with an int type, the specialization will look something like this:

int add_em_up(int& lhs, int& rhs) {
    return lhs + rhs;
}

The specialization takes the template and replaces all instances of the T placeholder with the type from the invocation, in this case, int. The compiler creates a separate specialization of the template each time it's invoked with a different type.

STL containers, like vector, stack, or map, along with their iterators and other supporting functions and algorithms, are built with templates so they can be used generically while maintaining type safety. This is what makes the STL so flexible. Templates are the T in the STL.

This book uses the C++20 standard

The C++ language is standardized by the International Organization for Standardization (ISO) on a roughly three-year cycle. The current standard is called C++20 (which was preceded by C++17, C++14, and C++11 before that). C++20 was approved in September 2020.

C++20 adds many important features to the language and the STL. New features like format, modules, ranges, and more will have significant impact on the way we use the STL.

There are also convenience changes. For example, if you want to remove every matching element of a vector, you may have been using the erase-remove idiom like this:

auto it = std::remove(vec1.begin(), vec1.end(), value);
vec1.erase(it, vec1.end());

Starting with C++20 you can use the new std::erase function and do all of that in one simple, optimized function call:

std::erase(vec1, value);

C++20 has many improvements, both subtle and substantial. In this book, we will cover much of it, especially what's relevant to the STL.

Braced initialization

You may notice that the recipes in this book often use braced initialization in place of the more familiar copy initialization.

std::string name{ "Jimi Hendrix" };  // braced initialization
std::string name = "Jimi Hendrix";   // copy initialization

The = operator pulls double-duty as both an assignment and a copy operator. It's common, familiar, and it works, so we've all been using it forever.

The downside of the = operator is that it's also a copy constructor, which often means implicit narrowing conversion. This is both inefficient and can lead to unintended type conversions, which can be difficult to debug.

Braced initialization uses the list initialization operator {} (introduced in C++11) to avoid those side effects. It's a good habit to get into and you'll see it a lot in this book.

It's also worth noting that the special case of T{} is guaranteed to be zero-initialized.

int x;      // uninitialized            bad  :(
int x = 0;  // zero (copy constructed)  good :)
int x{};    // zero (zero-initialized)  best :D

The empty brace zero initialization offers a useful shortcut for initializing new variables.

Hiding the std:: namespace

In most instances, the exercises in this book will hide the std:: namespace. This is mostly for page space and readability considerations. We all know that most STL identifiers are in the std:: namespace. I will normally use some form of the using declaration to avoid cluttering the examples with repetitive prefixes. For example, when using cout you can presume I've included a using declaration like this:

using std::cout;    // cout is now sans prefix
cout << "Hello, Jimi!\n"; 

I usually will not show the using declaration in the recipe listings. This allows us to focus on the purpose of the example.

It is poor practice to import the entire std:: namespace in your code. You should avoid a using namespace declaration like this:

using namespace std;    // bad. don't do that. 
cout << "Hello, Jimi!\n"; 

The std:: namespace includes thousands of identifiers and there's no good reason to clutter your namespace with them. The potential for collisions is not trivial, and can be hard to track down. When you want to use a name without the std:: prefix, the preferred method is to import a single name at a time, as above.

To further avoid namespace collisions, I often use a separate namespace for classes that will be re-used. I tend to use namespace bw for my personal namespace. You may use something else that works for you.

Type aliases with using

This book uses the using directive for type aliases instead of typedef.

STL classes and types can be verbose at times. A templated iterator class, for example, may look like this:

std::vector<std::pair<int,std::string>>::iterator

Long type names are not just hard to type, they are prone to error.

One common technique is to abbreviate long type names with typedef:

typedef std::vector<std::pair<int,std::string>>::iterator vecit_t

This declares an alias for our unwieldy iterator type. typedef is inherited from C and its syntax reflects that.

Beginning with C+11, the using keyword may be used to create a type alias:

using vecit_t = std::vector<std::pair<int,std::string>>::iterator;

In most circumstances, a using alias is equivalent to typedef. The most significant difference is that a using alias may be templated:

template<typename T>
using v = std::vector<T>;
v<int> x{};

For these reasons, and for the sake of clarity, this book prefers the using directive for type aliases.

Abbreviated function templates

Beginning with C++20, an abbreviated function template may be specified without the template header. For example:

void printc(const auto& c) {
    for (auto i : c) {
        std::cout << i << '\n';
    }
}

The auto type in a parameter list works like an anonymous template typename. It is equivalent to:

template<typename C>
void printc(const C& c) {
    for (auto i : c) {
        std::cout << i << '\n';
    }
}

Though new in C++20, abbreviated function templates have been supported by the major compilers for some time already. This book will use abbreviated function templates in many of the examples.

The C++20 format() function

Until C++20 we've had a choice of using legacy printf() or the STL cout for formatting text. Both have serious flaws but we've used them because they work. Beginning with C++20, the format() function provides text formatting inspired by Python 3's formatter.

This course uses the new STL format() function liberally. Please see Chaper 1, New C++20 Features, for a more comprehensive description.

Use the STL to solve real-world problems

The recipes in this book use the STL to provide real-world solutions to real-world problems. They have been designed to rely exclusively on the STL and C++ standard libraries, with no external libraries. This should make it easy for you to experiment and learn without the distractions of installing and configuring third-party code.

Now, let's go have some fun with the STL. Happy learning!