Articles & Books

More on Harmful Overuse of std::move -- Raymond Chen

RaymondChen_5in-150x150.jpgIn recent discussions around the use of std::move in C++, questions have arisen regarding its potential overuse and the compiler's treatment of its return values. Addressing concerns raised by developers like Jonathan Duncan, this article delves into the nuances of std::move, examining whether its current implementation aligns with compiler optimizations and proposing potential enhancements for more efficient code generation.

More on harmful overuse of std::move

by Raymond Chen

From the article:

Some time ago, I wrote about harmful overuse of std::move. Jonathan Duncan asked,

Is there some side-effect or other reason I can’t see return std::move(name); case isn’t possible to elide? Or is this just a case of the standards missing an opportunity and compilers being bound to obey the standards?

In the statement return std::move(name);, what the compiler sees is return f(...); where f(...) is some mysterious function call that returns an rvalue. For all it knows, you could have written return object.optional_name().value();, which is also a mysterious function call that returns an rvalue. There is nothing in the expression std::move(name) that says, “Trust me, this rvalue that I return is an rvalue of a local variable from this very function!”

Now, you might say, “Sure, the compiler doesn’t know that, but what if we made it know that?” Make the function std::move a magic function, one of the special cases where the core language is in cahoots with the standard library.

This sort of in-cahoots-ness is not unheard of. For example, the compiler has special understanding of std::launder, so that it won’t value-propagate memory values across it, and the compiler has special understanding of memory barriers, so that it won’t optimize loads and stores across them.

Qt and Trivial Relocation (Part 2) -- Giuseppe D'Angelo

kdab.pngIn this installment we are going to explore the relationships between trivial relocation and move assignments.

Qt and Trivial Relocation (Part 2)

by Giuseppe D'Angelo

From the article:

Last time we started our investigation of trivial relocation by considering an important use-case: reallocating a vector. This happens when a vector reaches its capacity, but more storage is needed.

Let’s now consider a different operation: erasing an element from the middle of a QVector.

How do we go about it?

isocpp-dangelo.png

Pulling a Single Item From a C++ Parameter Pack by its Index -- Raymond Chen

RaymondChen_5in-150x150.jpgThis article explores techniques to access specific elements within a C++ parameter pack by index. It delves into the use of std::tie for creating a tuple of lvalue references and explains how std::forward_as_tuple can preserve the original reference categories of the parameters. Additionally, it highlights a proposed feature in C++26, Pack Indexing, which aims to simplify this process significantly.

Pulling a Single Item From a C++ Parameter Pack by its Index

by Raymond Chen

From the article:

Suppose you have a C++ parameter pack and you want to pluck out an item from it by index.

template<int index, typename...Args>
void example(Args&&... args)
{
    // how do I access the index'th args parameter?
}

One solution is to use std::tie:

template<int index, typename...Args>
void example(Args&&... args)
{
    auto& arg = std::get<index>(
        std::tie(args...));
}

Why Can’t I Find the Injected Name of a Templated Class’s Templated Base Class? -- Raymond Chen

RaymondChen_5in-150x150.jpgSome time ago, I wrote about how injected class names were the C++ feature you didn’t even realize that you were using. Injected class names let you use the plain name for the class being defined without needing to fully qualify it with namespaces and template parameters. Furthermore, injected class names are public and can be inherited.

“But wait, I’m trying to use the injected class name of my base class, but the compiler won’t accept it.”

Why Can’t I Find the Injected Name of a Templated Class’s Templated Base Class?

by Raymond Chen

From the article:

Some time ago, I wrote about how injected class names were the C++ feature you didn’t even realize that you were using. Injected class names let you use the plain name for the class being defined without needing to fully qualify it with namespaces and template parameters. Furthermore, injected class names are public and can be inherited.

“But wait, I’m trying to use the injected class name of my base class, but the compiler won’t accept it.”

template<typename T>
struct Base
{
    Base(T value);
};

template<typename T>
struct Derived : Base<T>
{
    Derived(T value) : Base(value) {}
};

This generates a compiler error.

 

An Informal Comparison of the Three Major Implementations of std::string -- Raymond Chen

RaymondChen_5in-150x150.jpgWe saw some time ago that the three major implementations of std::string are all quite different...

An informal comparison of the three major implementations of std::string

by Raymond Chen

From the article:

In the original version of this article, I got the sense of the “small/large” bit backward in the clang implementation. This in turn led to redoing the code generation and new code golfing results.

We’ll compare these versions based on the complexity of some commonly-used operations.

Adding State to the Update Notification Pattern, Part 7 -- Raymond Chen

RaymondChen_5in-150x150.jpgLast time, we refined our change counter-based stateful but coalescing update notification. This version still relies on a UI thread to do two things: (1) make the final final change counter check and the subsequent callback atomic, and (2) to serialize the callbacks.

Adding State to the Update Notification Pattern, Part 7

by Raymond Chen

From the article:

If we don’t have a UI thread, then we open a race condition.

 
class EditControl
{
    ⟦ ... existing class members ... ⟧

    std::atomic<unsigned> m_latestId;
};

winrt::fire_and_forget
EditControl::TextChanged(std::string text)
{
    auto lifetime = get_strong();

    auto id = m_latestId.fetch_add(1, std::memory_order_relaxed);

    co_await winrt::resume_background();

    if (!IsLatestId(id))) co_return;

    std::vector<std::string> matches;
    for (auto&& candidate : FindCandidates(text)) {
        if (candidate.Verify()) {
            matches.push_back(candidate.Text());
        }
        if (!IsLatestId(id))) co_return;
    }

    // co_await winrt::resume_foreground(Dispatcher());

    if (!IsLatestId(id))) co_return;

    SetAutocomplete(matches);
}

Understand Internals of std::expected -- Bartlomiej Filipek

BartlomiejFilipek-expected.pngIn the article about std::expected, I introduced the type and showed some basic examples, and in this text, you’ll learn how it is implemented.

Understand Internals of std::expected

by Bartlomiej Filipek

From the article:

In short, std::expected should contain two data members: the actual expected value and the unexpected error object. So, in theory, we could use a simple structure:

template <class _Ty, class _Err> 
struct expected {  
     /*... lots of code ... */  
     _Ty _Value;  
     _Err _Unexpected; 
}; 

However, there are better solutions than this. Here are some obvious issues for our “struct” approach.

  • The size of the object is the sum of the Value type and the Error type (plus padding if needed).
  • Two data members are “active” and initialized, which might not be possible - for example, what if the Value type has no default constructor? The Standard requires that std::expected" holds either a value of type Tor an error of typeE` within its storage.
  • We’d have to guarantee that _Ty cannot be a reference type or an array type; it must be a Destructible Type.
  • Similarly for the _Err type we have to guarantee that it’s also Destructible, and must be a valid template argument for std::unexpected (so not an array, non-object type, nor cv-qualified type).
  • Plus, we’d have to write a lot of code that creates an API for the type

C++ programmer's guide to undefined behavior: part 1 of 11

Your attention is invited to the first part of an e-book on undefined behavior. This is not a textbook, as it's intended for those who are already familiar with C++ programming. It's a kind of C++ programmer's guide to undefined behavior and to its most secret and exotic corners. The book was written by Dmitry Sviridkin and edited by Andrey Karpov.

C++ programmer's guide to undefined behavior: part 1 of 11

by Dmitry Sviridkin

From the article:

Many modern programming languages, especially newer ones, forbid implicit type conversions. So, in Rust, Haskell, or Kotlin, we can't just use float and int in the same arithmetic expression without explicitly stating in the code to convert one to the other. Python isn't as strict but still keeps strings, characters, and numbers from mixing. C++ doesn't forbid implicit conversion, which leads to a lot of erroneous code. Moreover, such code can contain both defined (but unexpected) and undefined behavior.

Qt and Trivial Relocation (Part 1) -- Giuseppe D'Angelo

sso1.pngIn Qt 4, container classes like QVector introduced an optimization that transformed certain operations on contained objects into efficient byte-level manipulations. By identifying types that can be safely moved via a simple memory copy, Qt was able to streamline reallocations for specific data types like int and QString. This article explores the concept of trivial relocation, how Qt leverages it for optimized data manipulation, and the implications for different container structures and data types.

Qt and Trivial Relocation (Part 1)

by Giuseppe D'Angelo

From the article:

The container classes introduced in Qt 4 (Tulip, for the aficionados) had an interesting optimization: the ability to turn certain operations on the contained objects into byte-level manipulations.

Example: vector reallocation

Consider the reallocation of a QVector<T>: when the vector is full and we want to insert a new value (of type T), the vector has to allocate a bigger block of memory.

vector_realloc_step0.png

Error on verge of extinction, or why I put if (x = 42) in Red List of C & C++ bugs

If we ask a programmer what bugs are the most common in C and C++ code, they'll name a null pointer dereference, undefined behavior, array overrun, and other typical error patterns. They may name an accidental assignment in condition as well. However, let's see if this error is common today.

Error on verge of extinction, or why I put if (x = 42) in Red List of C & C++ bugs

by Andrey Karpov

From the article:

Because of this bug, developers invented the Yoda notation: a programming style where the constant is placed on the left side of the comparison operator. This style was meant to prevent a typo. If a programmer writes = instead of ==, the code won't compile.