C ++ 20 approved! What to expect and what to prepare for developers in C ++ 23

The other day in Prague, a meeting of the international standardization committee C ++ took place. And-and-and-and ...



C ++ 20 is ready! It remains to put a stamp from ISO, but this is a purely formal step with which there should be no problems.

Congratulations to all on this wonderful event! Concepts, Coroutines, Modules, Ranges, std :: format, constexpr new and constexpr algorithms + vector + string, datetime, jthread, span, bit_cast and many other small and big innovations.

What they managed to add and fix at the last moment, what they proposed to break and what everyone wants to see in C ++ 23 - about all this under the cut.

Tricks from C to C ++ 20


Recently, there has been a tradition of scaring novice developers with undefined behavior (UB) in C ++. It's time to change it!

Here, for example, such code is absolutely valid for C:

struct X { int a, b; };

X *make_x() {
  X *p = (X*)malloc(sizeof(struct X));
  p->a = 1;
  p->b = 2;
  return p;
}

But in C ++ there were big problems with it. C operates with bytes, and C ++ works with objects. But the object has a lifetime, and before C ++ 20, the beginning of life for the object was considered from the call new.

The committee is seriously concerned about the low-level work with bytes and simple structures. They adopted improvements that say that a certain set of functions (memcpy, memmove, malloc, aligned_alloc, calloc, realloc, bit_cast) begins the lifetime of the object. Now most of the low-level C-tricks are guaranteed to work in C ++.

bool has become more reliable in C ++ 20


Guess what the error is:

template <typename T, size_t N>
auto count_unique(const std::array<T, N>& v) {
    return std::unordered_set<T>{v.begin(), v.end()}.size();
}

Answer
T bool v.begin() v.end() (, libc++ libstdc++) — count_unique 1.

- , std::unordered_set<bool>{v.begin(), v.end()} bool std::unordered_set<bool>{true, true}.

Transforms in bool are now considered narrowing transformations. This allowed us to find problems in many codebases (more examples in the P1957 sentence itself ).

C ++ 20 concepts made faster


Until recently, you could write a concept like this:

template <class T>
concept Reservable = requires(T v) {
    v.reserve(int{});
};

And be surprised that it returns different results:

struct Test;

static_assert(!Reservable<Test>);

struct Test {
    void reserve(int);
};

static_assert(Reservable<Test>);

Last time, we sent a comment from the country: “Make it impossible to use incomplete type in concepts, because otherwise you get multiple ODR violations”. Our comment was rejected, but we partially got the desired result now with proposal P2104.

As a bonus, we get faster compilation, since compilers are now entitled to cache the results of applying concepts to types.

Minor edits in C ++ 20


  • Ranges got the ssize method.
  • Entities internal linkage are no longer visible when instantiating module linkage entities (the compiler will tell you what is wrong at the compilation stage).
  • We twisted the rules for the modules so that it was easier for any tool to work with them.

And let's break everything in C ++ 23 or C ++ 26?


Prolonged debate has sparked a proposal for an Application Binary Interface (ABI, do not confuse with the API). Interesting questions were raised:

1. We can completely change ABI in C ++ 23 and get 5-10% performance gain.


Moreover, all the old C ++ libraries will have to be rebuilt; they will not be able to link to libraries with the new ABI. You cannot use libraries built by earlier versions of C ++ in a C ++ 23 project.

Well, of course, there will always be old commercial software that no one will reassemble, but that will drag its standard library (yes, video games, I'm talking about you!).

With a slight margin of vote, ABI decided not to break in C ++ 23.

2. Let's give users a guarantee that we will try not to break / change the ABI.


And then they decided not to give a guarantee. Different vendors have different plans for their platforms, and sometimes they can afford to break the ABI, often without harm to users.

And let's add everywhere noexcept?


Historically, in the standard functions with preconditions are not marked as noexcept, even if they never throw exceptions. Here, for example, operator -> have std :: optional:

constexpr const T* operator->() const;
constexpr T* operator->();
    Requires: *this contains a value.
    Returns: val.
    Throws: Nothing.

He doesn’t throw anything, but instead of noexcept it says in words that “Throws: Nothing”, because there is a precondition “* this contains a value”.

Users with noexcept will be clearer. A good idea in P1656 !



No!

There is a whole subgroup SG21: Contracts, which comes up with a common mechanism for checking contracts (pre- and postconditions). Contract handlers can throw an exception, if an exception is thrown from the noexcept function, it will be std :: terminate and the application will crash. If you insert a special crutch that for contracts exceptions can fly out of a noexcept function ... then everything breaks down anyway, type traits are guided by the presence of noexcept, and they start to lie to you, a function marked with noexcept with a precondition will throw an exception.

But this is not the biggest problem. There are forks of standard libraries, which already now in some cases explicitly insert precondition checks. For example, do you have a critical project, the availability of which should be maximized. You use a similar fork, and if someone suddenly called std :: vector :: back () for an empty vector, an exception throws, which is processed higher in the code and fallback starts to be used. With edits from P1656, such a library can no longer be considered standard.

And this is not all the problems! .. Not only will the additional noexcept for the standard library bring any positive effects in the form of a decrease in the size of binary files or more performance, not only will the change break the code of at least two companies, not only will it destroy one of the ways use of contracts ... so also the proposal was approved in two subgroups.

Merits of RG21


As always, we worked in various subgroups, shared implementation experience, presented proposals, the authors of which could not come.

One of the outstanding ideas that we were lucky to present was the idea of ​​Anton Zhilin P2025 Guaranteed copy elision for named return objects . Its implementation will allow creating factory functions for objects without copy and move constructors. In fact, this is a destructive move, which has secretly existed in the standard since the mid-90s and was specifically prohibited by individual language rules.

We successfully dragged the idea through the EWG-I and EWG instances thanks to the excellent elaboration of the idea by the author himself. The CWG stage remained, and after a couple of meetings there is every chance to see the necessary words in the standard, and the first implementations in compilers.

In addition to this idea, we dragged the idea of P1990R0: Add operator [] to std :: initializer_list via LEWG-I, got a useful feedback on P1944R0: constexpr <cstring> and <cwchar> . Both ideas of Daniil Goncharov have every chance of being in C ++ 23.

In the field of std :: hash, an unexpected failure awaited us. The discussion of p1406r1: Add more std :: hash specializations suddenly turned into a discussion of degenerate boundary cases and the possibilities of distant C ++ 2 *. As a result, the committee decided not to change anything.

With SG6 and Numbers did not grow together. The main discussions of SG6 intersected with discussions of ABI, which is why the quorum in SG6 did not accumulate. Because of this p1889: C ++ Numerics Work In Progress , P2010: Remove iostream operators from P1889 andP1890: C ++ Numerics Work In Progress Issues were not discussed.

C ++ 23 Plans


Since the beginning of the development of C ++ 20, the committee began to act according to plan. Namely, to identify several major interesting ideas for the next standard, after which at all subsequent meetings not to consider proposals on other topics, if not all have been discussed on the main one.

For C ++ 23, such a plan was just approved in Prague. The main priorities of C ++ 23:

  1. Corutin support in the standard library
  2. Convert standard library to modules
  3. Executors
  4. Networking

In the first paragraph, everyone will be guided by the CppCoro library . So if you already want to use C ++ 20 coroutines, you should start by using this library.

With the modules, the second point, you just need to sit down and do it, no special difficulties are expected.

But Executors is a problem. Their design is not obvious, not all user cases are covered, in their current form they were not used by anyone, and the design is still not approved.

The committee also agreed to prioritize proposals in the following areas:

  • Reflection
  • Pattern matching
  • Contracts

Instead of totals


C ++ 20 is ready, it's time to work on C ++ 23! The next Committee meeting will be in the summer, so if you have a decent idea of the new standard - share them on stdcpp.ru and Telegram-chatting ProCxx .

Well, everyone who wants to chat with representatives of the committee live - look at the meetings and C ++ conferences *:


* Life hack: you do not have to pay for a conference ticket if you are a speaker.

All Articles