Articles & Books

Inside STL: Smart Pointers -- Raymond Chen

RaymondChen_5in-150x150.jpgThe C++ standard library comes with a few smart pointer types. The simplest one is unique_ptr: This class babysits a raw pointer and remembers to delete it at destruction (in an appropriate manner). Dumping the contents of a unique_ptr is just looking at the raw pointer inside.

Inside STL: Smart Pointers

By Raymond Chen

From the article:

The C++ standard library comes with a few smart pointer types.

The simplest one is unique_ptr: This class babysits a raw pointer and remembers to delete it at destruction (in an appropriate manner). Dumping the contents of a unique_ptr is just looking at the raw pointer inside.

The complication is that there is also a deleter object in the unique_ptr. This deleter object is usually an empty class, so it is stored as part of a compressed pair.

The Visual Studio debugger has a visualizer that understands unique_ptr, but here’s what it looks like at a low level in the Microsoft implementation:

0:000< ?? p
class std::unique_ptr<S,std::default_delete<S> >
   +0x000 _Mypair          : std::_Compressed_pair<std::default_delete<S>,S *,1>
0:000< ?? p._Mypair
class std::_Compressed_pair<std::default_delete<S>,S *,1>
   +0x000 _Myval2          : 0x0000020a`11f08490 S
0:000< ?? p._Mypair._Myval2
struct S * 0x0000020a`11f08490 ← here is the unique object
   +0x000 a                : 0n42

Inside STL: The array -- Raymond Chen

RaymondChen_5in-150x150.jpgThe C++ standard library array is just a C-style array wrapped inside a class so that it behaves like a normal C++ object instead of a wacky thing that undergoes decay.

Inside STL: The array

By Raymond Chen

From the article:

template<typename T, size_t N>
class array
{
    T elements[N];
};

template<typename T>
class array<T, 0>
{
};

The only weird case is N = 0. You are allowed to create a zero-length std::array, but C++ does not allow zero-length C-style arrays. The zero-length std::array is just an empty class.

Visual Studio and the Windows debugger come with a visualizer:

0:000> dx a
a                : { size=5 } [Type: std::array<int,5>]
    [<Raw View>]     [Type: std::array<int,5>]
    [0]              : 3 [Type: int]
    [1]              : 1 [Type: int]
    [2]              : 4 [Type: int]
    [3]              : 1 [Type: int]
    [4]              : 5 [Type: int]

Meeting C++ 2023 - the last online conference?

Highlighting the online part of Meeting C++ 2023

Meeting C++ 2023 - the last online conference?

by Jens Weller

From the article:

A few days ago it caught my attention that Meeting C++ 2023 would be the last C++ organisation with an online part of its conference.

This is the case because last year the online part did find its interest in the C++ community. I think it serves the goals of Meeting C++ to continue an online presence for the conference as long as this is feasible...

C++20 Dynamic Allocations at Compile-time -- Andreas Fertig

logo.pngPeople often say constexpr all the things. Andreas Fertig shows where we can use dynamic memory at compile time.

C++20 Dynamic Allocations at Compile-time

By Andreas Fertig

From the article:

You may already have heard and seen that C++20 brings the ability to allocate dynamic memory at compile-time. This leads to std::vector and std::string being fully constexpr in C++20. In this article, I like to give you a solid idea of where you can use that.

How does dynamic allocation at compile-time work?

First, let’s ensure that we all understand how dynamic allocations at compile-time work. In the early draft of the paper ‘Standard containers and constexpr’ [P0784R1], proposed so-called non-transient allocations. They would have allowed us to allocate memory at compile-time and keep it to run-time. The previously allocated memory would then be promoted to static storage. However, various concerns did lead to allowing only transient allocations. That means what happens at compile-time stays at compile-time. Or in other words, the dynamic memory we allocate at compile-time must be deallocated at compile-time. This restriction makes a lot of the appealing use-cases impossible. I personally think that there are many examples out there that are of only little to no benefit.

Passkey Idiom: A Useful Empty Class -- Arne Mertz

logo.pngHow do you share some but not all of a class? Arne Mertz introduces the passkey idiom to avoid exposing too much with friendship.

Passkey Idiom: A Useful Empty Class

By Arne Mertz

From the article:

Let’s have a look at an example for useful empty classes. The passkey idiom can help us regain the control that we give up by simply making classes friends.

The problem with friendship

Friendship is the strongest coupling we can express in C++, even stronger than inheritance. So, we’d better be careful and avoid it if possible. But sometimes we just can’t get around giving one class more access than another.

A common example is a class that has to be created by a factory. The factory needs access to the class’s constructors. Other classes should not have that access so as not to circumvent the bookkeeping or whatever else makes the factory necessary.

The problem with the friend keyword is that it gives access to everything. There is no way to tell the compiler that the factory should not have access to any other private elements except the constructor. It’s all or nothing. 

Inside STL: The deque, implementation -- Raymond Chen

RaymondChen_5in-150x150.jpgNow that we understand the design behind the common STL dequeue implementations, we can peek into the implementation details.

Inside STL: The deque, implementation

By Raymond Chen

From the article:

All three of the major implementations of the standard library maintain an array of pointers to blocks, which they confusingly call a “map” even though it is unrelated to std::map. (Even more confusingly, gcc internally uses the term “node” instead of “block”.) Initially, all the pointers in the map are nullptr, and the blocks are allocated only on demand.

We will say that a block is spare if it contains only spare elements.

Inside STL: unordered_map, unordered_set, unordered_multimap, & unordered_multiset -- Raymond Chen

RaymondChen_5in-150x150.jpgThe C++ standard library provides hash-based associative containers unordered_mapunordered_setunordered_multimap, and unordered_multiset.

All of these collections are hash tables with different payloads. The unordered_map and the unordered_multimap use a std::pair<Key, Value> as the payload, whereas the the unordered_set and the unordered_multiset use a Key as the payload.

Inside STL: The unordered_map, unordered_set, unordered_multimap, and unordered_multiset

By Raymond Chen

From the article:

Conceptually, the hash table consists of a bunch of buckets, and each bucket contains a linked list of the nodes that fall into that bucket. (This design is known as open hashing or separate chaining.) However, that’s not how the information is structured internally, because iterating through a traditionally-structured hash table requires more state than a single pointer: When you reach the end of each hash chain, you need some other information to tell you which chain to enumerate next.

C++ standard library implementations instead structure the hash table like this:

struct hashtable
{
    using hint = std::list<payload>::iterator;

    std::list<payload> list;
    std::vector<hint> buckets;
};

The list is a linked list of payloads, sorted by bucket. The buckets is a vector of iterators (pointers) into the list that tells you where each bucket begins. Each bucket implicitly ends when the next bucket begins, or (for the last bucket) when the end of the list is reached.

Inside STL: The deque, design -- Raymond Chen

RaymondChen_5in-150x150.jpgThe C++ standard library deque is a double-ended queue that supports adding and removing items efficiently at either the front or the back.

Inside STL: The deque, design

By Raymond Chen

From the article:

All three of the major implementations of the C++ standard library use the same basic structure for a deque, but they vary in both policy and implementation details.

First, let’s design a simple version of a deque that stores its elements in an array.

template<typename T>
struct simple_deque
{
    T* elements;
    T* first;
    T* last;
    size_t capacity;
};

For example, a deque of three integers might look like this:

capacity = 8
size = last − first = 3
 

The elements points to an array whose length is given by the capacity member’s value of 8. In that array, the first three elements are not in use, but which could be used in the future. We’ll call them spares. Next come three elements holding the values 1, 2, and 3, followed by two more spares. The first element in use (1) is pointed to by first, and one past the last element in use is pointed to by last.

C++ Exceptions and Memory Allocation Failure -- Wu Yongwei

logo.pngMemory allocation failures can happen. Wu Yongwei investigates when they happen and suggests a strategy to deal with them.

C++ Exceptions and Memory Allocation Failure

By Wu Yongwei

From the article:

C++ exceptions are habitually disabled in many software projects. A related issue is that these projects also encourage the use of new (nothrow) instead of the more common new, as the latter may throw an exception. This choice is kind of self-deceptive, as people don’t usually disable completely all mechanisms that potentially throw exceptions, such as standard library containers and string. In fact, every time we initialize or modify a stringvector, or map, we may be allocating memory on the heap. If we think that new will end in an exception (and therefore choose to use new (nothrow)), exceptions may also occur when using these mechanisms. In a program that has disabled exceptions, the result will inevitably be a program crash.

However, it seems that the crashes I described are unlikely to occur… When was the last time you saw a memory allocation failure? Before I tested to check this issue, the last time I saw a memory allocation failure was when there was a memory corruption in the program: there was still plenty of memory in the system, but the memory manager of the program could no longer work reliably. In this case, there was already undefined behaviour, and checking for memory allocation failure ceased to make sense. A crash of the program was inevitable, and and it was a good thing if the crash occurred earlier, whether due to an uncaught exception, a null pointer dereference, or something else.

Now the question is: If there is no undefined behaviour in the program, will memory allocation ever fail? This seems worth exploring.

Five Advanced Initialization Techniques in C++ -- Bartlomiej Filipek

cppstories-5advinit-fi.pngFrom dynamic container operations to compile-time constants, C++ offers a variety of techniques. In this article, we’ll delve into advanced initialization methods likereserve() and emplace_backfor containers to tuples with piecewise_construct and forward_as_tuple. Thanks to those techniques, we can reduce the number of temporary objects and create variables more efficiently.

Five Advanced Initialization Techniques in C++: From reserve() to piecewise_construct and More

By Bartlomiej Filipek

From the article:

As a background, we can use the following class that will be handy to illustrate when its special member functions are called. That way, we’ll be able to see extra temporary objects.