In September last year, the ISO committee approved C++ 20 as the current version of the international standard. I suggest that you familiarize yourself with the most useful and long-awaited changes to the new standard.

C++ Concepts Library

The library defines fundamental concepts that can be used to dispatch functions and check template arguments at compile time, based on type properties. The concepts are needed in order to avoid logical contradictions between the properties of the data types within the template and those of the input parameters. The concept should be defined within the namespace and is as follows.

Each concept is a predicate that is evaluated at compile time and becomes part of the template interface, where it is used as a constraint:

The #include directives are followed by the declaration of the Sorter concept, which any type T satisfies such that for values a of type T the expression std::hash{}(a) is compiled, and its result is converted to std::size_t. If we call f(asdf) in main, we get a completely meaningful compilation error.

The compiler also converts the concept, like requires-expression into a bool value, and then they can be used as a simple value, for example, in if constexpr.

Requires-expression

The new keyword in C++ 20 has two meanings: requires clause and requires-expression. Despite the significant payload, this duality of requires leads to confusion.

Requires-expression uses the bool type, and the code in curly braces is evaluated at compile time. If the expression is correct, requires-expression returns true, otherwise it returns false. The first oddity is that the code in curly braces must be written in a specially designed language, not C++.

The main use case for requires-expression is to create concepts, just check for the required fields and methods within the type.

However, requires-expression has other uses. It is often necessary to check whether a given set of template parameters provides the required interface: free functions, member functions, related types, etc.

Requires clause

To really limit something, we need a requires clause. It can be applied to any templated declaration or non-templated function to see if it is visible in a particular context. The main benefit of requires clause is that it lets you forget about SFINAE and other weird C++ templating workarounds.

In the requires clause declaration, it is possible to use several predicates combined by the logical operators && or ||.

Because of the dual nature of the requires keyword, situations with reference unreadable code can arise.

The same requires requires, the first is clause, the second is expression.

Modules

There is a long-term trend in C++, which is expressed in the gradual elimination of the preprocessor. It is believed that this will eliminate a number of difficulties:

  • headers depending on the order of inclusion;
  • leakage of macros from header files;
  • re-compilation of the same code;
  • cyclical dependencies;
  • poor encapsulation of implementation details.

For example, source_location replaces one of the most commonly used macros, and consteval replaces macro functions. The new way to split source code uses modules and is intended to completely replace all #include directives.

This is what a modular Hello World!..

Coroutines

A coroutine is a function that can stop execution in order to be resumed later. Such a function does not have a stack, it pauses execution, returning to the calling instruction. C++ 20 provides almost the lowest-level API, leaving everything else to the user’s discretion.

A function is a coroutine if one of the following is used in its definition.

  • co_await statement to pause execution until resuming;

  • the co_yield keyword to suspend execution, returning a value;

  • the co_return keyword to complete execution that returns a value.

Coroutines cannot use simple return statements, auto, or Concept types, and variable arguments.

KK operator

C++ 20 introduced the three-way comparison operator <=> and immediately got the nickname spaceship operator, which means spaceship operator. This operator for two variables a and b defines one of three: a>b, a=b or a<b. You can define the <=> operator yourself, or the compiler will automatically generate one for you.

The easiest way to understand with an example is what exactly the new three-way comparison operator is needed for.

One gets the impression that the bool operator <… code is too much for a simple operator in order to avoid compilation errors. Well, if other operators are also needed: >, ==, ≤, ≥ it is inconvenient to display this entire block every time. Now, thanks to the <=> operator, we get the same thing in an easier way.

Note that we need an additional header file, so #include. In fact, we got more than we asked for, since now we can use all comparison operators at once, not just <.