Articles & Books

Data Structures and Algorithms with the C++ STL -- John Farrier

Explore the C++ STL with practical guidance on vectors, algorithms, and custom types for intermediate developers, enriched by real-world examples.

Data Structures and Algorithms with the C++ STL: A guide for modern C++ practitioners

by John Farrier

From the book:

While the Standard Template Library (STL) offers a rich set of tools for data structures and algorithms, navigating its intricacies can be daunting for intermediate C++ developers without expert guidance. This book offers a thorough exploration of the STL’s components, covering fundamental data structures, advanced algorithms, and concurrency features.

Starting with an in-depth analysis of the std::vector, this book highlights its pivotal role in the STL, progressing toward building your proficiency in utilizing vectors, managing memory, and leveraging iterators. The book then advances to STL’s data structures, including sequence containers, associative containers, and unordered containers, simplifying the concepts of container adaptors and views to enhance your knowledge of modern STL programming. Shifting the focus to STL algorithms, you’ll get to grips with sorting, searching, and transformations and develop the skills to implement and modify algorithms with best practices. Advanced sections cover extending the STL with custom types and algorithms, as well as concurrency features, exception safety, and parallel algorithms.

Using std::expected from C++23 -- Bartlomiej Filipek

c++stories-usingstd.pngIn this article, we’ll go through a new vocabulary type introduced in C++23. std::expected is a type specifically designed to return results from a function, along with the extra error information.

Using std::expected from C++23

by Bartlomiej Filipek

From the article:

Imagine you’re expecting a certain result from a function, but oops… things don’t always go as planned:
/*RESULT*/ findRecord(Database& db, int recordId) {
    if (!db.connected())
        return /*??*/
   
    auto record = db.query(recordId);
    if (record.valid) {
        return record;
	
    return /*??*/
}
What can you do? What should you return or output through /*RESULT*/ ?

C++23: Bitwise Operations -- Sandor Dargo

SANDOR_DARGO_ROUND.JPGWhile C++ is getting increasingly expressive with each new standard, we must not forget its origins. It is inherently a low-level language which operates close to the hardware level and allows operations that languages such as Javascript cannot even express.

A part of providing low-level functionalities is to be able to work on a byte or even on a bit level. In this post, we are going to see what related features C++23 brings or modifies.

C++23: Bitwise Operations

by Sandor Dargo

From the article:

constexpr std::bitset

To be fair, we already covered this earlier last year in C++23: Even more constexpr. But as I’m using this feature to demonstrate the next one, I decided to mention it again.

P2417R2 extends the constexpr interface of std::bitset. So far, only one of the constructors and operator[] was marked as constexpr. However, since std::string can be constexpr, all the internals - and therefore the full API - of std::bitset can be constexpr.

If you’re unfamiliar with std::bitset, it represents the object you pass in as a sequence of bits. You have to pass the number of bits as a non-type template argument.

#include <bitset>
#include <iostream>

int main()
{
    constexpr short i = 15;
    constexpr int numberOfBitsInInt = sizeof(i) * 8;
    std::cout << "i:" << i << ", i as binary: " << std::bitset<numberOfBitsInInt>(i) << '\n';
}
/*
i:15, i as binary: 0000000000001111
*/

It’s more capable than that, it also offers several ways to query the individual bits or set them.

C++23 likes to move it! -- Sandor Dargo

SANDOR_DARGO_ROUND.JPGC++23 is going to bring us a few changes regarding move operations. It mostly means extended support in the standard library, but there is also one change directly in the language. Let’s start with that.

C++23 likes to move it!

by Sandor Dargo

From the article:

P2266R3 is a quite great proposal both in terms of its high-quality explanation and the amount of proposed changes in wording. I think if you’re interested in exploring a readable proposal, this might be the one.

Let me try to summarize it briefly.

Since the introduction of move semantics and rvalue references in C++11, we can return move-only types by value:

struct Widget {
    Widget(Widget&&);
};

Widget one(Widget w) {
    return w;
}
C++14 extended this support so that even converting constructors accepting an rvalue type can be called to invoke an implicit move.
struct RRefTaker {
    RRefTaker(Widget&&); // here is the converting constructor
};

RRefTaker two(Widget w) {
    return w;
}

As you can see, the two examples...

Parsing Numbers At Compile Time with C++17, C++23, and C++26 -- Bartlomiej Filipek

modern-parsingnum.pngThanks to the powerful constexpr keyword and many enhancements in recent C++ standards, we can now perform a lot of computations at compile time. In this text, we’ll explore several techniques for parsing integers, including the “naive” approach, C++23,from_chars,std::optional, std::expected, and even some upcoming features in C++26.

Parsing Numbers At Compile Time with C++17, C++23, and C++26

by Bartlomiej Filipek

From the article:

Why at compile time?

While it may sound like a theoretical experiment, since C++11 we can shift more and more computations to compile-time. Here are some key areas and examples where constexpr can be beneficial:

Starting easy from C++17 

Starting with C++17, we are now capable of writing complex constexpr functions. However, our ability to do so is still limited by the range of algorithms available in that context. Luckily, with the introduction of string_view in that version of C++, there is no longer any need to work with “raw” const char* buffers.

Class Invariants -- Andrzej Krzemieński

Andrzej-Krzemieński.pngThis article explores the concept of class invariants in C++ and their significance in maintaining code integrity and abstraction. It highlights the difference between struct and class definitions and discusses the role of class invariants in guaranteeing the correctness of class objects. The article also touches upon the trade-offs between strong and weak invariants and provides insights into when to define a new class with proper invariants.

Class Invariants

by Andrzej Krzemieński

From the article:

The primary motivation for defining a class in C++ is to reflect and maintain a class invariant. In this post we will see what class invariants are and how you deal with them. Class invariants are important part of C++, even though there is no “invariant” keyword in C++.

Contrast the following two class definitions:

struct Point
{
  int x;
  int y;
};
class Range
{
  int _min;
  int _max;

public:
  // ...
};

In C++ a struct is practically a class but with a different default member access. The first is an aggregate: it only allows two pieces of data to travel together. If it was not for the nice member names, we might have as well used std::pair<int, int> instead. 

Optimizing the Unoptimizable: A Journey to Faster C++ Compile Times -- Victor Zverovich

zverovich-compiletimes.jpgIn this post, Victor talks about bringing compile times of the {fmt} library on par with the C standard I/O library (stdio).

Optimizing the Unoptimizable: A Journey to Faster C++ Compile Times

by Victor Zverovich

From the article:

First some background: {fmt} is a popular open-source formatting library for C++ that provides a better alternative to C++ iostreams and C stdio. It has already surpassed stdio in many areas:

  • Type safety with compile-time format string checks available by default since C++20 and as an opt in for C++14/17. Runtime format strings are also safe to use in {fmt} which is impossible to achieve in printf.
  • Extensibility: user-defined type can be made formattable and most standard library types such as containers, dates and times are formattable out of the box.
  • Performance: {fmt} is significantly faster than common standard library implementations of printf, in some cases by an order of magnitude (e.g. on floating-point formatting).
  • Portable Unicode support.

However, one area where stdio remained significantly better was compile times.