An Introductory Tutorial for Modern C++

This site contains an original, self-contained guide to learning the fundamentals of Modern C++, intended to be the basis of a self-study course. Some Chapters depend upon material presented in earlier ones, so studying them linearly is recommended. There are also regular posts (accessible from the front page) which cover topics or areas of C++ not covered in the course.

If you have any queries or suggestions, please leave a comment. The course materials are in a completed state (note: recently updated to cover some parts of C++23) and are made freely available here:

  1. About this Tutorial
  2. String and Character Literals
  3. Variables, Scopes and Namespaces
  4. Conditions and Operators
  5. Functions
  6. Arrays, Pointers and Loops
  7. Enums and Structs
  8. Strings, Containers and Views
  9. Files and Formatting
  10. Classes, Friends and Polymorphism
  11. Templates, Exceptions, Lambdas, Smart Pointers

Last update: 2024/01/22

Using Lambda Functions for Delayed Evaluation

Lambda functions (sometimes called “anonymous functions”) have been a part of C++ for many years now, so usage of them should be well accepted. This mini-article is intended to outline a use case for using lambdas to perform a type of “lazy evaluation”, a term from FP (functional programming) languages. Lazy evaluation is not in fact very different from conventional evaluation, except in that the evaluation is delayed until the result is actually required. We will look at how this can be useful when working with (mutable) independent variables which are able to be changed (something usually not possible with FP).

Continue reading “Using Lambda Functions for Delayed Evaluation”

Exploring C++23’s flat_map

This article intends to examine how to utilize the container adapter std::flat_map, new to the C++23 Standard Library. You’re probably familiar with std::map and std::unordered_map (and their “multi” variants), and may be curious as to why we need another associative container type. The reason, as usual for C++, is performance: in some cases, operations on a std::flat_map can outperform those on a std::map or a (sorted) std::vector.

Continue reading “Exploring C++23’s flat_map”

Linear Algebra support in C++26

It’s likely that you’re already familiar with std::string_view and std::span<T> in Modern C++, each of which provides a lightweight “view” onto either string data or other contiguous typed data (such as made available by std::vector<T>::data()). A key difference is that std::string_view is immutable, even if initialized with non-const data. (You could instead use std::span<char> to get around this, if necessary.) A limitation of std::span<T> is that it only supports a single-dimensional view, so is not guaranteed to work with a construct like the following:

void f() {
  int array2d[2][3];
  std::span<int> arr_span{ reinterpret_cast<int*>(array2d), 6 };  
  // ...
}
Continue reading “Linear Algebra support in C++26”

Replacing the Preprocessor in Modern C++

Back in the early days when the original C++ compiler compiled into C, it seemed natural to use the existing C preprocessor to add C (and C++) headers to each source file in order to create a coherent compilation unit. With the Standard Library available as a module in the C++23 standard, it’s time we tried to use other features of Modern C++ to fulfil tasks we’ve previously relied on the C preprocessor for. This article aims to cover all of these main features.

Continue reading “Replacing the Preprocessor in Modern C++”

Selecting Functions at Runtime

Not all choices can be made at compile-time, sometimes including which function (of several possibilities) to invoke. This article aims to cover all of the methods available to Modern C++ when selecting which function to call, with the decision made at runtime (based upon user input, for example). Terms covered in this article include function pointers, function objects, virtual functions and lambdas.

Continue reading “Selecting Functions at Runtime”

Signaling error conditions without exceptions

In this article we’re going to look at a feature new to C++23 which supports a way to return an error condition from a function where a result value was anticipated. This is enabled in a new header <expected> which (as far as I am aware) does not require specific compiler support. (MSVC 19.37 with /std:c++latest was used to test the program code in this article.)

Continue reading “Signaling error conditions without exceptions”

Selecting Functions at Compile Time

When writing C++, we like to do as much of the work as possible at compile time; with C++ being a statically-typed language, we know the type(s) involved when compilation is taking place. In this article we’ll look at how to pass and receive, variables and results, of differing types depending on the nature of the function call itself. The four methods we will examine are: function overloading, template specialization, tag dispatch, and SFINAE.

Continue reading “Selecting Functions at Compile Time”

Concepts 101

Concepts finally appeared in the language with the completion of C++20—I say finally as work to specify them had been going on continuously since before the release of C++11. This article attempts to explain their rationale and usage, with examples that should compile on any up-to-date C++ compiler when specifying -std=c++20 (or /std:c++20 for MSVC).

So why do we need concepts when template syntax has been available since C++98? Put simply, templates have been (successfully) used in ways that were never envisaged; however the reporting of invalid instantiations and similar errors has not kept pace. Also, the use of auto in function parameter lists gives us template syntax “invisibly”, which the novice/intermediate coder may not expect.

Continue reading “Concepts 101”

Designing Traits and Policy Classes

Traits and policy classes are often used with C++ generics, but their role and purpose is often something of an enigma even to experienced C++ programmers. The definitions of these two terms overlap to some extent, and could be summarized as follows:

  • Traits represent natural additional properties of a template parameter.
  • Policies represent configurable behavior for generic functions and types.

In this article the example code will show use of both of these two, with type information obtained through a (specialized) traits class and functionality obtained from a (generic) policy class.

Continue reading “Designing Traits and Policy Classes”

Applying Functional Programming Techniques to Modern C++

Functional programming (FP) is a discipline and practice of computer science where Mathematical concepts come to the fore. There are a number of popular FP languages out there, but we can stay with C++ and explore some of the themes of FP by using a subset of familiar syntax. In this article we’ll look at a number of FP concepts, such as purity, immutability and use of higher-order functions, and how they can be concisely and usefully represented in Modern C++.

1. Purity

A function is considered pure if its operation depends only upon its formal input parameters, that is not accessing any external (global) state. Global state in FP is a little like the historical Goto statement, shunned but sometimes necessary, and so is by design abstracted away as much as possible. A C++ lambda is pure if it has an empty capture clause, for example:

Continue reading “Applying Functional Programming Techniques to Modern C++”

Designing Classes for Serialization (4)

In the previous articles of this mini-series we’ve covered the use of custom serialization and deserialization functions to generate and interpret XML. In this article we’ll look at how to add to existing classes to give them the ability to utilize the (de-)serialization logic already written. The process is similar to before: create an in-memory model and then serialize it, or start with blank containers and then read in the data, checking for errors.

Continue reading “Designing Classes for Serialization (4)”

Designing Classes for Serialization (3)

In the previous articles we’ve looked at creating an in-memory XML hierarchy (or DOM), and serializing from and deserializing to this memory model. In this article we’re going to look at querying the DOM in a similar fashion to JavaScript methods such as document.getElementById() available in a browser. The scheme chosen is to overload operator [] (twice) and provide other convenience methods such as name() and numberOfChildren().

Continue reading “Designing Classes for Serialization (3)”

Designing Classes for Serialization (2)

The complement of serialization is deserialization, and this is typically more difficult as it requires character- or pattern-matching of the input, plus checking for erroneous input. To match a stream of XML the possibility of using std::getline() was considered, but this is too inflexible as regards whitespace. Stream input to a std::string was also considered, but again this is unsuitable as breaking all input at whitespace is not what is needed.

Continue reading “Designing Classes for Serialization (2)”