Back in February, I attended the WG21 C++ standards committee meeting in rainy Kona, Hawaii (yes, it rained most of the week). This report is so late that we're now preparing for the next meeting, which will take place mid-July in Cologne.
As usual, I spent the majority of my time in the Library Working Group (for LWG; for details on the various Working Groups and Study Groups see Standard C++: The Committee). The purpose of the LWG is to formalize the specification of the C++ Standard Library, i.e. the second "half" of the C++ standard (although in terms of page count it's closer to three quarters than half). With a new C++20 standard on the horizon, and lots of new features that people want added to the standard library, the LWG has been very busy trying to process the backlog of new proposals forwarded by the Library Evolution Working Group (LEWG).
One of the main tasks at the Kona meeting was to review the "Ranges Design Cleanup" proposal. The cleanup involves a number of fixes and improvements to the new Ranges library, addressing issues that came up during the review of the previous (much larger) proposal to add the Ranges library, which is one of the biggest additions to the C++20 library (most of the other significant additions to C++20 affect the core language, without much library impact). In fact, I'd say it's one of the biggest additions to the C++ standard library since the first standard in 1998. The Ranges library work overhauls the parts of the standard that originated in the Standard Template Library (STL), i.e. iterators, algorithms, and containers, to re-specify them in terms of C++ Concepts. This has been a multi-year effort that has now landed in the C++20 working draft, following multiple proposals and several meetings of wording review by LWG.
Another item on the agenda was the C++20 Synchronization Library. This is an omnibus paper that combines several earlier proposals for new synchronization primitives, such as waiting on atomic variables, semaphores, and latches and barriers. Because it's a combination of proposals written by different authors at different times, the wording is quite inconsistent and different parts of the proposal try to say the same thing in completely different ways. We found numerous issues with the wording and asked the authors to revise the proposal and bring it back. Personally, I think it's unfortunate that the independent proposals were combined because some parts are in good shape and could have moved forward, but now everything in the proposal is waiting for a few pieces to be fixed.
We also spent a good chunk of time reviewing "A Standard
flat_map." This is a proposal for a new container that has a similar API to
std::map but stores the data in an array-like structure rather than a binary tree. The current proposal is for a container adaptor, so that you can create a flat_map from a
std::deque, or other container. Since the first version, the proposal has been altered so that a
flat_map uses separate containers for the keys and the values; thus, all the keys are packed together into one array and all the values into another, improving the locality of reference for the keys. The downside of splitting the keys and values is that it's trickier to specify, and the LWG review revealed lots of exception safety issues. We made numerous improvements to the wording and clarified what the implementation can guarantee in corner cases. In some cases, if an operation fails, the only reasonable way to preserve the invariants is to clear the container completely to restore it to a known state. I'm not convinced that the advantage of separating keys and values outweighs the disadvantages. An updated proposal will be reviewed again by LWG, and I'm pleased to see the paper "Issues with current
flat_map proposal" in the most recent mailing, which discusses some of the disadvantages of the current design.
Mandating the Standard Library
At the Kona meeting, and between meetings, we've also been reviewing a series of proposals by Marshall Clow (the LWG chair) all on the same topic, "Mandating the Standard Library" (this is a pun on the fact that we now use the label "Mandates" in the specification to describe statically enforced preconditions). This is a topic dear to my heart as I wrote the first attempt to address this topic four years ago, in "Separating Library Requirements and Preconditions." The aim is to recategorize every precondition in the standard library so that those which can be statically checked and enforced at compile time will result in compilation failure rather than arbitrary undefined behavior. Historically, the standard library simply says it's undefined to violate most of its preconditions, even though in practice violating a requirement such as "the template argument T is CopyConstructible" will fail to compile, rather than invoke the dreaded "undefined behavior."
In the next version of the standard, users will be able to clearly distinguish requirements that will cause compilation failure from preconditions that must be met to avoid your printer catching fire or demons flying out of your nose. In Kona, we reviewed the first papers in the series, covering Clause 16 - Language support library, Clause 18 - Diagnostics library, Clause 20 - Strings library, Clause 21 - Containers library, and Clause 22 - Iterators library. At the upcoming Cologne meeting, we'll be reviewing papers to do the same thing for Atomics operations, Thread support, the Time library, Algorithms, Numerics, Localization, I/O, Regular expressions, and Atomic operations (yes, there are two papers for this clause—due to miscommunication by the authors—so now we get to see if they've proposed the same fixes). If we get through all of those in Cologne, that should be all the library clauses covered. Then we just need to make sure that new proposals use the new specification style, so we don't add back any of the old "everything is undefined behavior!" preconditions.
We also reviewed (and approved) "Well-behaved interpolation for numbers and pointers," which adds
std::lerp to the library. These functions are surprisingly hard to get right for all the corner cases, and discussions continued long after the meeting about how to implement them. The fact that even domain experts (and non-experts trying to implement them, like me) struggled to get them right makes them perfect candidates for inclusion in the standard library. It's better to have a few compiler vendors deal with the problem once, rather than having thousands of users write them again and again, either creating subtle bugs or duplicating effort to deal with all the gotchas properly.
We also reviewed (and approved) "Signed
ssize() functions, unsigned
size() functions" (P1227R2), which addresses a controversial topic that has seen some heated debate. The containers in the standard library use unsigned types (typically
size_t) for sizes and indices. It is widely (but not universally) believed that this was a mistake and that signed types should have been used. Using a signed type (e.g., POSIX's
ssize_t instead of
size_t) makes detecting overflow more reliable, because sanitizers and assertions can tell when a positive value overflows to a negative one. It's more difficult to detect when an unsigned value wraps, because that's well-defined by the rules of the language—unsigned types can't overflow, they wrap instead. It's not always possible to tell when "a very large value" is obviously too large and due to a bug (whereas a negative index into a
std::vector is always wrong).
std::span class template was first added to the C++20 draft, it broke with tradition and used signed types. This caused some consternation, even among those who thought that signed types were a better choice in theory, because consistency with every other type in the library was felt by many to be more important (see P1089R2 for more information). The compromise approach taken by P1227R2 is to consistently use unsigned types, but add a new
ssize() free function that can be used to get the size of a container as a signed type. This might not be the last we hear of this topic, but we seem to have consensus for now.
I think those are the most interesting papers processed by LWG. If you're interested, the following papers not covered here were also approved for inclusion in C++20:
- Making std::underlying_type SFINAE-friendly
- I Stream, You Stream, We All Stream for istream_iterator
- Make create_directory() Intuitive
- Vectorization Policies from Parallelism V2 TS
- Usability Enhancements for std::span
- Precalculated hash values in lookup
- Traits for [Un]bounded Arrays
The status of GCC's experimental support for the C++20 library is tracked in the libstdc++ documentation.
I also made a few brief trips outside the LWG room, when there were discussions of papers I was interested in (or had helped author).
In the LEWG Incubator, I took part in yet another discussion of the
colony proposal, which I've written about before. It still has some supporters who think it's interesting and useful (myself included), but others want more justification. The outcome of the latest review was the desire for more certainty that this fairly novel data structure would fully meet real needs (rather than be almost what people need, but different enough that they can't actually use it). The proposal hasn't moved forward, but it's definitely not dead either and we'll see it again. I also participated in some LEWG Incubator discussions of problems with the Networking TS (which I'm the project editor for). We had the author of Boost.Asio and the Networking TS present to respond to the issues raised in P1100, P1133, and P1145, which was useful. Alternative solutions to those issues are being worked on.
I also attended a session in the EWG Incubator, for a paper I co-authored proposing a language extension to simplify working with parameter packs in tuples and similar structures. This paper didn't proceed but also isn't dead yet and will likely return for another round of discussion.
Finally, I attended a LEWG discussion on
source_location, and then later in the week the LWG review of that proposal (where it got bogged down in signed/unsigned discussion, which happens a lot recently, see P1227R2 above.)
That's all for the Kona report. Later this month, I'll be in Cologne for the next meeting. The LWG agenda for Cologne includes an updated
flat_map proposal, as well as a first review of its companion proposal for
flat_set, and an update to the C++20 synchronization proposal discussed above.