Summary / TL;DR (new developments since last meeting in bold)
Project | What’s in it? | Status |
C++20 | See below | On track |
Library Fundamentals TS v3 | See below | Under active development |
Concepts TS | Constrained templates | Merged into C++20, including abbreviated function templates! |
Parallelism TS v2 | Task blocks, library vector types and algorithms, and more | Published! |
Executors | Abstraction for where/how code runs in a concurrent context | Not headed for C++20 |
Concurrency TS v2 | See below | Under active development |
Networking TS | Sockets library based on Boost.ASIO | Published! Not headed for C++20. |
Ranges TS | Range-based algorithms and views | Merged into C++20! |
Coroutines TS | Resumable functions, based on Microsoft’s await design |
Merged into C++20! |
Modules v1 | A component system to supersede the textual header file inclusion model | Published as a TS |
Modules v2 | Improvements to Modules v1, including a better transition path | Merged into C++20! |
Numerics TS | Various numerical facilities | Under active development |
Reflection TS | Static code reflection mechanisms | Approved for publication! |
C++ Ecosystem TR | Guidance for build systems and other tools for dealing with Modules | Early development |
Pattern matching | A match -like facility for C++ |
Under active development, targeting C++23 |
Introduction
A few weeks ago I attended a meeting of the ISO C++ Standards Committee (also known as WG21) in Kona, Hawaii. This was the first committee meeting in 2019; you can find my reports on 2018’s meetings here (November 2018, San Diego), here (June 2018, Rapperswil), and here (March 2018, Jacksonville). These reports, particularly the San Diego one, provide useful context for this post.
This week marked the feature-complete deadline of C++20, so there was a heavy focus on figuring out whether certain large features that hadn’t yet merged into the working draft would make it in. Modules and Coroutines made it; Executors and Networking did not.
Attendance at this meeting wasn’t quite at last meeting’s record-breaking level, but it was still quite substantial. We continued the experiment started at the last meeting of running Evolution Incubator (“EWGI”) and Library Evolution Incubator (“LEWGI”) subgroups to pre-filter / provide high-level directional guidance for proposals targeting the Evolution and Library Evolution groups (EWG and LEWG), respectively.
Another notable procedural development is that the committee started to track proposals in front of the committee in GitHub. If you’re interested in the status of a proposal, you can find its issue on GitHub by searching for its title or paper number, and see its status — such as which subgroups it has been reviewed by and what the outcome of the reviews were — there.
C++20
Here are the new changes voted into C++20 Working Draft at this meeting. For a list of changes voted in at previous meetings, see my San Diego report. (As a quick refresher, major features voted in at previous meetings include default comparisons (<=>
), concepts, contracts, and ranges.)
- Language:
- Modules! In what is undoubtedly the highlight not only of this meeting, but the entire C++20 release cycle, Modules was merged into the C++20 working draft. This follows the design approval achieved at the last meeting, and thorough wording review in between, including fixing some issues discovered in the process. To recap, the design that was approved (and now merged) combines aspects the Modules TS design and Clang Modules, and includes facilities for incrementally transitioning codebases to Modules. There are outstanding concerns about the impact of the feature on the C++ tooling ecosystem; I talk about this more below.
- Merging the Coroutines TS into C++20. After being bogged down in controversy for much of the past year, as the committee considered several alternative coroutine proposals, the Coroutines TS finally gained consensus for merger into C++20 at this meeting. I talk a bit more about it below.
- Allow initializing aggregates from a parenthesized list of values. This is an important change, as it allows things like
make_unique()
andvector::emplace_back()
to work with aggregates. It had a bit of a bumpy ride, as it was discovered that making it behave exactly as if the aggregate type had an invented constructor (which was the direction as of the last meeting) had unexpected implications; the final semantics are a mixture of brace initialization and a real constructor call. <=> != ==
, an important fix to the default comparisons design.- Extending structured bindings to be more like variable declarations.
- Reference capture of structured bindings.
- Contract postconditions and return type deduction.
- Array size deduction in new-expressions. This is also a Defect Report against previous versions of C++.
- Contra CWG DR1778 (a bugfix related to
noexcept
and explicitly defaulted functions). - Make
char16_t
/char32_t
string literals be UTF-16/32.
- Library:
polymorphic_allocator<>
as a vocabulary type.- Well-behaved interpolation for numbers and pointers., a.k.a.
std::midpoint
- Signed
ssize()
functions, unsignedsize()
functions inspan
- I stream, you stream, we all stream for
istream_iterator
. - Ranges design cleanup
- Target vectorization policies (from the Parallelism TS v2)
- Usability enhancements for
std::span
- Make
create_directory()
intuitive. - Precalculated hash values in lookup
- Traits for [un]bounded arrays
- Making
std::underlying_type
SFINAE-friendly.
Technical Specifications
In addition to the C++ International Standard (IS), the committee publishes Technical Specifications (TS) which can be thought of experimental “feature branches”, where provisional specifications for new language or library features are published and the C++ community is invited to try them out and provide feedback before final standardization.
At this meeting, the committee iterated on a number of TSes under development.
Reflection TS
The Reflection TS was sent out for its PDTS ballot two meetings ago. As described in previous reports, this is a process where a draft specification is circulated to national standards bodies, who have an opportunity to provide feedback on it. The committee can then make revisions based on the feedback, prior to final publication.
The ballot results (often referred to as “NB comments”, as they are comments from national bodies in response to the ballot) were published between the last meeting and this one, and the TS authors prepared proposed resolutions, which various subgroups reviewed this week. I am pleased to report that the committee addressed all the comments this week, and subsequently voted to publish the TS as amended by the comment resolutions. The final draft is not prepared yet, but I expect it will be in the committee’s next mailing, and will then be transmitted to ISO for official publication.
(I mentioned previously that a procedural snafu necessitated rebasing the TS onto {C++17 + Concepts TS} as it could not reference the not-yet-published C++20 working draft which contains Concepts in their current form. I was slightly mistaken: as the Concepts TS, which was published in 2015, is based on C++14, the Reflection TS actually had to be rebased onto {C++14 + Concepts TS}. Geneva: 1, common sense: 0.)
I wish I could tell you that there is an implementation of the Reflection TS available for experimentation and encourage you to try it out. Unfortunately, to my knowledge there is no such implementation, nor is one imminent. (There is a WIP implementation in a clang branch, but I didn’t get the impression that it’s actively being worked on. I would be delighted to be mistaken on that point.) This state of affairs has led me to reflect (pun intended) on the TS process a bit.
Library Fundamentals TS v3
This third iteration (v3) of the Library Fundamentals TS is under active development, and gained its first new feature at this meeting, a generic scope guard and RAII wrapper. (The remaining contents of the TS working draft are features from v2 which haven’t been merged into the C++ IS yet.)
Merging Technical Specifications into C++20
This meeting was the deadline for merging published TSes into C++20, so naturally a large amount of attention on the outstanding ones such as Modules and Coroutines.
Modules TS
As mentioned in my previous report, Modules gained design approval at the end of the last meeting, in San Diego. This was the culmination of a multi-year effort to reconcile and merge two different approaches to Modules — the design from the Modules TS, which has its roots in Microsoft’s early implementation work, and the Atom proposal which was inspired by Clang Modules — into a unified and cohesive language feature.
I found it interesting to see how the conversation around Modules shifted as the two approaches achieved convergence. For much of the past few years, the discussions and controversies focused on the differences between the two proposals, such as macro support, incremental transition mechanisms, and module organization (preambles and partitions and such).
Now that the compiler implementers have achieved consensus on the language feature, the focus has shifted to parts of the C++ ecosystem outside of the compilers themselves that are affected by Modules — notably, build systems and other tools. The tooling community has a variety of outstanding concerns about Modules, and these concerns dominated the conversation around Modules at this meeting. I talk about this in more detail in the SG15 (Tooling) section below, but my point here is that consensus among compiler implementers does not necessarily imply consensus among the entire C++ community.
All the same, the Core Working Group proceeded with wording review of Modules at full speed, and it was completed in time to hold a plenary vote to merge the feature into C++20. As mentioned, this vote passed, in spite of concerns from the tooling community. That is to say, Modules are now officially in the C++20 working draft!
It’s important to note that this does not mean the committee doesn’t care about the tooling-related concerns, just that it has confidence that the concerns can be addressed between now and the publication of C++20 (or, in the case of issues whose resolution does not require a breaking change to the core language feature, post-C++20).
Coroutines TS
The proponents of the Coroutines TS have been trying to merge it into C++20 for quite some time. Each of the three previous meetings saw an attempt to merge it, with the latter two making it to a plenary vote, only to fail there. The reason it had failed to achieve consensus so far was that there were some concerns abouts its design, and a couple of alternative proposals which attempt to address those concerns (namely, Core Coroutines, which had been under development for a few meetings now, and a new one at this meeting, Symmetric Coroutines).
We are sufficiently late in the cycle that the alternative proposals had no chance of getting into C++20, so the decision the Committee needed to make is, are the improvements these alternatives purport to bring worth delaying the feature until C++23 or later. Thus far, the Committee had been delaying this decision, in the hopes that further development on the alternative proposals would lead to a more informed decision. With this meeting being the deadline for merging a TS into C++20, the opportunities for delay were over, and the decision needed to be made this week.
Knowing that we’re down to the wire, the EWG chair instructed the authors of the various proposals to collaborate on papers exploring the design space, putting the respective proposals into context, and comparing their approaches in detail.
The authors delivered on this request, with commendably thorough analysis papers. I talk about the technical issues a bit below, but the high level takeaways were as follows:
- Both alternative proposals share an implementation challenge inherent to their attempt to expose the state of a coroutine as a first-class object, that would have signficant language impact. While compiler implementers agreed the proposals are implementable, they estimated the magnitude of the language impact to be sufficiently great that the ability to work out the specification issues and deliver an implementation in the C++23 timeframe was uncertain (that is, going with the alternatives would risk Coroutines being delayed until C++26).
- Due to the relative novelty of the alternative proposals, as compared to the Coroutines TS which has multiple implementations and deployment experience, meant there was much less certainty about their eventual success, as there may be issues with them yet to be discovered. (As an example, the implementation challenge mentioned above only really came to be understood at this meeting.)
- At least some of the advantages the alternative proposals would bring to the Coroutines TS could be accomplished via incremental, non-breaking changes post-C++20 (though this would also come with costs, such as greater complexity).
Importantly, all of the authors were more or less in agreement on these points; their differences remained only in the conclusions they drew from them.
This allowed the Committee to make what I believe was a well-informed final decision, which was that merging the Coroutines TS into C++20 gained consensus both in EWG and subsequently in plenary. Notably, it wasn’t “just barely consensus,” either — the ratio of the final vote in plenary was on the order of 10 in favour to 1 against.
Networking TS
The Networking TS did not make C++20, in part due to concerns about its design based on usage experience, and in part because it depends on Executors which also didn’t make it (not even a subset, as was hoped at the last meeting).
Musings on the TS Process
Disclaimer: This section reflects my personal opinions on potentially controversial topics. Caveat lector / feel free to skip / etc.
Recall that using Technical Specifications as a vehicle to allow large proposals to mature before final standardization is a procedural experiment that the Committee embarked on after C++11, and which is still ongoing. I’ve mentioned that opinions on how successful this experiment has been, vary widely within the Committee.
I’ve previously characterized Concepts and Modules as examples of success stories for the TS process, as both features improved significantly between their TS and IS forms.
However, one realization has been on my mind of late: we don’t seem to have a great track record for motivating compiler vendors to implement language features in their TS form. Let’s survey a few examples:
- Concepts was only implemented in its TS form by GCC. As far as I’m aware, Clang implementation efforts have specifically targeted their C++20 form only.
- To my knowledge, the Modules TS does not have a complete implementation either; it was partially implemented in MSVC and Clang, but both efforts have since moved on to target the newer, C++20-track formulation.
- As mentioned above, the Reflection TS does not have a complete implementation, nor is one being actively worked on. Implementation efforts again seem to be focused on the newer,
constexpr
-based reflection facilities that are targeting C++23.
(If I’m mistaken on any of these points, I apologize in advance; please do point it out in a comment, and I will amend the above list accordingly.)
The Coroutines TS, which has multiple shipping implementations, is a notable exception to the above pattern. Library TS’es such as Networking, Filesystem, Library Fundamentals, and Parallelism also have a good track record of implementation. The fact remains, though, that the majority of our core language TS’es have not managed to inspire complete implementations.
This somewhat calls into question the value of language TS’s as vehicles for gathering use experience: you can’t collect use experience if users don’t have an implementation to use. (By contrast, implementation experience can be gathered from partial implementation efforts, and certainly has been for both Concepts and Modules.)
It also calls into question claims along the lines of “choosing to standardize [Feature X] as a TS first doesn’t mean you [users] have to wait longer to get it; you can just use the TS!” — a claim that I admit to have made myself, multiple times, on this blog.
What are the takeaways from this? Are language TS’es still a good idea? I’m still trying to work that out myself, but I will suggest a couple of takeaways for now:
- Implementations can move faster than standards. Language TS’es are often snapshots of a rapidly evolving design. By the time a TS is published, its design is often known to have important flaws, and often it’s already been iterated on. Compilers don’t have much of a motivation to polish an implementation of a known-to-be-broken thing, nor users a motivation to use it.
- Large features take a long time to get right. To take Modules as an example: while the Modules TS didn’t end up being something people can really use in practice, it seems to me that pushing Modules into the C++17 IS would been a mistake as well; given the extent of the feature’s evolution between then and now, locking the design as it stood in ~2016 (the C++17 feature-complete date) into the IS would have resulted in a significantly less baked feature. That suggests to me, that perhaps the choice we gave ourselves back then (“Modules: TS, or C++17?”) was a false choice. Perhaps a better choice would have been to continue iterating on Modules until it’s ready, even if that meant not publishing any spec-like document about Modules in the 2017 timeframe. (Update: see below for a counter-argument.)
Perhaps the actionable suggestion here is to downplay the role of a TS as a way to get a core language feature in front of users early. They do play other roles as well, such as providing a stabilized draft of a feature’s specification to write proposed changes against, and arguably they remain quite useful in that role.
Update: since publishing this, I’ve received private feedback that included suggestions of other advantages of core language TS’es, which I’ve found compelling, and wanted to share:
- They allow Core wording review of a feature to proceed even while there are outstanding design questions (which can be deferred to post-TS consideration), which can in turn result in important issues being discovered and resolved sooner.
- They prod implementers by putting them on notice that the feature may be standardized in this form in the absence of feedback. While this may not lead to complete implementations of the TS, it often does lead to partial implementation efforts that generate very valuable feedback.
Continuing with the example of Modules, both of the above considerations were in play, and contributed to the high quality of the feature now headed for C++20.
Evolution Working Group
I spent most of the week in EWG, as usual, although I did elope to some Study Group meetings, and to EWGI for a day.
Here I will list the papers that EWG reviewed, categorized by topic, and also indicate whether each proposal was approved, had further work on it encouraged, or rejected. Approved proposals are targeting C++20 unless otherwise mentioned; “further work” proposals are not, as this meeting was the deadline for EWG approval for C++20.
Contracts
Contracts — which have been added into the C++20 working draft at the last meeting — have been the subject of very extensive mailing list discussions, and what I understand to be fairly heated in-person debates in EWG. I wasn’t in the room for them (I was in EWGI that day), but my understanding is that issues that have come up related to (1) undefined behaviour caused by compilers assuming the truth of contact predicates, and (2) incremental rollout of contracts in a codebase where they may not initially be obeyed at the time of introduction, have led to a plurality of stakeholders to believe that the Contracts feature as currently specified is broken.
To remedy this, three different solutions were proposed. The first two — “Avoiding undefined behaviour in contracts” and “Contracts that work” — attempted to fix the feature in the C++20 timeframe, with different approaches.
The third proposal was to just remove Contracts from C++20.
However, none of these proposals gained EWG consensus, so for now the status quo — a feature believed to be a broken in the working draft — remains.
I expect that updated proposals to resolve this impasse will forthcome at the next meeting, though I cannot predict their direction.
EWG did manage to agree on one thing: to rename the context-sensitive keywords that introduce pre- and post-conditions from expects
and ensures
(respectively) to pre
and post
. Another proposed tweak, to allow contract predicates on non-first decarations, failed to gain consensus.
Modules
EWG reviewed a handful of Modules-related proposals:
- (Approved) Constrained internal linkage for modules. This is a design fix that supersedes two other proposals related to linkage, “Modules: ADL & internal linkage” and “Module partitions are not a panacea”, by making code that would run into the underlying issues ill-formed. Notably, the proposal requires a diagnostic when the rules it introduces are violated; I’m very glad of this, as I find the “ill-formed, no diagnostic required” semantics that pervade the language (particularly those where there is actual implementation divergence on whether or not an error diagnostic is issued) a significant pitfall.
- (Sent to SG15) Implicit module partition lookup. This aims to address part of the tooling-related concerns around Modules by introducing a standard mechanism by which module names are resolved to file names. EWG felt that it would be more appropriate for this proposal to target the C++ Ecosystem TR rather than the IS, and accordingly forwarded the paper to SG 15 (Tooling).
- (Further work) Module resource dependency propagation. This is a proposal to allow annotating modular source files with the filenames of resource files (think e.g. an image that the code in the source file needs to use, that is shipped with an application) that they depend on, with the annotation appearing on the module declaration; the idea is that these annotations could inform a build system which could extract them and make them part of its dependency graph. EWG was sympathetic to the objectives but recognized that a proposal like this has specification challenges, as the current standard says very little about aspects of the host environment in which translation (building the program) takes place.
A couple of informational papers were also looked at:
- Are Modules fast? attempts to characterize the performance impact of Modules via a microbenchmark. The gist of the results is that Modules tend to increase throughput while potentially also increasing latency, depending on the shape of your program’s dependency graph, an observation which is also corroborated by real-world deployment experience.
- Make me a module describes an experimental implementation of build system support for Modules in GNU
make
.
Coroutines
Coroutines was probably the most talked-about subject at this meeting. I summarized the procedural developments that led to the Coroutines TS ultimately being merged into C++20, above.
Preceding that consequential plenary vote was an entire day of design discussion in EWG. The papers that informed the high-level directional discussion included:
- The alternative proposals: Core Coroutines and Symmetric Coroutines.
- Two papers outlining how something that accomplishes many of the goals of the alternative proposals can be built on top of the Coroutines TS in a backwards compatible fashion (the first of these is the “unified” proposal by Facebook that I mentioned in my last report).
- Two analysis papers comparing the various approaches in detail, one focused on use cases and the second on language and implementation impact.
- An experience report about implementing a coroutine TS frontend to an existing tasking library.
I’d say the paper that had the biggest impact on the outcome was the analysis paper about the language and implementation impact. This is what discussed, in detail, what I described above as an “implementation challenge” shared by Core Coroutines and Symmetric Coroutines. The issue here is that both of these proposals aim to expose the coroutine frame — the data structure that stores the coroutine state, including local variables that persist across suspension points — as a first-class object in C++. The reason this is challenging is that first-class C++ objects have certain properties, such as their sizeof
being known at constant expression evaluation time, which happens in the compiler front-end; however, the size of a coroutine frame is not known with any reasonable amount of accuracy until after optimizations and other tasks more typically done by the middle- or back-end stages of a compiler. Implementer consensus was that introducing this kind of depedency of the front-end on optimization passes is prohibitive in terms of implementation cost. The paper explores alternatives that involve language changes, such as introducing the notion of “late-sized types” whose size is not available during constant expression evaluation; some of these were deemed to be implementable, but the required language changes would have been extensive and still required a multi-year implementation effort. (The problem space here also has considerable overlap with variable-length arrays, which the committee has not been able to agree on to date.)
This, I believe was the key conclusion that convinced EWG members that if we go with the alternatives, we’re not likely to have Coroutines until the C++26 timeframe, and in light of that choice, to choose having the Coroutines TS now.
EWG also looked at a couple of specific proposed changes to the Coroutines TS, both of which were rejected:
- (Rejected) The trouble with
coroutine_traits
. This would have enhanced the ability of a programmer to customize the behaviour of a third-party coroutine type. I think the main reason for rejection was that the proposal involved new syntax, but the specific syntax had not been decided on, and there wasn’t time to hash it out in the C++20 timeframe. The proposal may come back as an enhancement in C++23. - (Rejected) Coroutines TS simplifications. There weren’t strong objections to this, but ultimately proceeding with the TS unmodified had the greater consensus as it has implementation experience.
constexpr
The march to make ever more things possible in constexpr
continued this week:
- (Approved) Permitting trivial default initialization in
constexpr
contexts. “Trivial default initialization” refers to things likeint x;
at local scope, which leavesx
uninitialized. This is currently ill-formed in aconstexpr
context; this proposal relaxes it so that it’s only ill-formed if you actually try to read the uninitialized value. The interesting use cases here involve arrays, such as the one used to implement a small-vector optimization. - (Approved) Adding the
constinit
keyword. This is a new keyword that can be used on a variable declaration to indicate that the initial value must be computed at compile time, without making the variableconst
(so that the value can be modified at runtime). - (Further work)
constexpr
structured bindings. This is targeting C++23; EWG didn’t request any specific design changes, but did request implementation experience. - (Rejected) An update on “More
constexpr
containers”. This proposal was previously approved by EWG, and had two parts: first, allowing dynamic allocation during constant evaluation; and second, allowing the results of the dynamic allocation to survive to runtime, at which time they are considered static storage. Recent work on this proposal unearthed an issue with the second part, related to what is mutable and what is constant during the constant evaluation. The authors proposed a solution, but EWG found the solution problematic for various reasons. After lengthy discussion, people agreed that a better solution is desired, but we don’t have time to find one for C++20, and the “promotion to static storage” ability can’t go forward without a solution, so this part of the proposal was yanked and will be looked at again for C++23. (The first part, dynamic allocations without promotion to static storage, remains on track for C++20.)
Comparisons
- (Approved)
<=> != ==
. This is a revised version of a proposal approved at the last meeting to fix usability issues with<=>
. The revisions were motivated by concerns brought up during Core wording review. The chosen direction from among the alternatives presented in the paper was that defaulting<=>
always declared a default==
as well. - (Approved) When do you actually use
<=>
. Another<=>
fix revised after Core wording review. The revisions were accepted with the tweak that strong and weak ordering are both synthesized from==
and<
only. - (Further work) Ambiguity and insecurities with three-way comparisons. It was agreed that the author would merge efforts with a related library proposal to make
strong_order
a customization point.
Pattern matching
- (Further work) Pattern matching. This is one of the most exciting proposals to look forward to in C++23; it will bring a pattern matching facility comparable to that in Rust and other modern languages, to C++. EWG spent most of an afternoon on it and gave the authors a lot of guidance, including on syntax choices, parseability, readability, and the proposed customization point design.
- (Rejected) Disallow
_
usage in C++20 for pattern matching in C++23. By the same authors as the pattern matching proposal, this paper tried to land-grab the_
identifier in C++20 for future use as a wildcard pattern in C++23 pattern matching. EWG wasn’t on board, due to concerns over existings uses of_
in various libraries, and the availability of other potential symbols. This does mean that the wildcard pattern in C++23 pattern matching will (very likely) have to be spelt some other way than_
.
Other new features
- (Approved) Expansion statements. This is a form of compile-time
for
loop that can iterate over tuple-like objects,constexpr
ranges, and parameter packs. The feature has been revised to address previous EWG feedback and use a single syntax,for...
- (Approved)
using enum
. This allows bringing all the enumerators of an enumeration, or just a specific enumerator, into scope such that they can be referenced without typing the enumeration name or enclosing type name. Approved with the modification that it acts like a series of using-declarations.
Bug / Consistency Fixes
(Disclaimer: don’t read too much into the categorization here. One person’s bug fix is another’s feature.)
- (Approved)
char8_t
backwards compatibility remediation. This contains a couple of minor, library-based mitigations for the backwards compatibility breakage caused byu8
literals changing from typechar
tochar8_t
. Additional library and language-based mitigations were mentioned but not proposed. - (Approved) Reference capture of structured bindings. Value capture was already approved at the previous meeting.
- (Approved) Implicit creation of objects for low-level object manipulation. This is largely standard wording changes to make certain widely-used code patterns, such as using
malloc()
to allocate a POD object, defined. It also introduces a new “barrier operation”std::bless
, a kind of counterpart tostd::launder
, which facilities writing custom operations that implicitly create objects likemalloc()
. One point that came up during discussion is that this proposal makes things like implementing a small vector optimization inconstexpr
possible (recall that things which trigger undefined behaviour at runtime are ill-formed during constant evaluation). - (Approved) Deprecating
volatile
. Despite the provocative title, which is par for the course from our esteemed JF Bastien, this only deprecates uses ofvolatile
which have little to no practical use, such asvolatile
-qualified member functions. - (Approved) Layout-compatibility and pointer-interconvertibility traits. This allows checking at compile time whether certain operations like converting between two unrelated pointer types would be safe.
- (Approved)
[[nodiscard("should have a reason")]]
. This extends the ability to annotate an attribute with a reason string, which[[deprecated]]
already has, to[[nodiscard]]
. - (Approved) More implicit moves. This extends the compiler’s ability to implicitly move rather than copy an object in some situations where it knows the original is about to go out of scope anyways. (The suggested future extension regarding assignment operators was not encouraged.)
- (Further work) Ultimate copy elision. This is an ambitious proposal to give compilers license to elide copies in cases not covered by the as-if rule (i.e. cases where the compiler can’t prove the elision isn’t observable; this is typically the case when the copy constructor being invoked isn’t entirely inline). The benefits are clear, but there are concerns that the proposed changes are not sound; more analysis and implementation experience is needed.
Proposals Not Discussed
Notable among proposals that didn’t come up this week is Herb’s static exceptions proposal. As this is a C++23-track proposal, it was deliberately kept out of EWG so far to avoid distracting from C++20, but it is expected to come up at the next meeting in Cologne.
Evolution Working Group Incubator
The EWG Incubator group (EWGI), meeting for the second time since its inception at the last meeting, continued to do a preliminary round of review on EWG-bound proposals.
I wasn’t there for most of the week, but here are the papers the group forwarded to EWG:
- Guidelines for when a WG21 proposal should be reviewed by SG16
- Deducing
this
(which was previously seen by EWG) - Literal suffixes for
ptrdiff_t
andsize_t
- A proposal for a type trait to detect scoped enumerations
- On the non-uniform semantics of return-type-requirements
Numerous other proposals were asked to return to EWGI with revisions. I’ll call out a few particularly interesting ones:
- Overload sets as function parameters. Being able to pass around overload sets has been proposed and shot down numerous times before. The novely in this approach is that it’s opt-in at the callee side, not the caller side.
- Parametric expressions. This is an ambitious proposal that aims to bring a sort of a hygienic macro system to C++.
- Object relocations in terms of move plus destroy. This aims to solve some common performance issues in the implementation of container types, where for some types of objects, relocating them to newly allocated storage can safely be done via a
memcpy
rather than invoking a move constructor and destructor, but the infrastructure for identifying such types is not present. - Language variants. This would add a core-language sum type, similar to Rust’s
enum
s, to C++, as an alternative to the library-basedstd::variant
.
Other Working Groups
Library Groups
Having sat in the Evolution groups, I haven’t been able to follow the Library groups in any amount of detail, but I’ll call out some of the more notable library proposals that have gained design approval at this meeting:
- Text formatting (
std::format
), includingchrono
integration. - Ranges / algorithms
- Range construtors for
string_view
andspan
ranges::to
- Input range adaptors
- Move-only views
- Moveability of single-pass iterators. This is significant because it allows us to drop the pesky requirement that iterators be
Semiregular
(which includes copyability) in many places. find_backward
- Range construtors for
- Minimal standard-library Modules for C++20. This basically just reserves some future module names, and mandates that standard library headers be importable into modules as header units (formerly called legacy header units; the legacy terminology was dropped due to its negative connotations). Actual modularization of the standard library is left for a future standard.
- Comparisons
- Make
strong_order
a customization point. This allows you to define a user-defined strong order on a type even if its default order might be e.g. just partial. - Integrating
<=>
into the standard library.
- Make
- New types
source_location
flat_set
ostream_joiner
jthread
(cooperatively interruptible joining thread, including stop tokens)unique_function
(move-onlystd::function
)out_ptr
(a scalable output pointer abstraction)
- Oher
Study Groups
SG 1 (Concurrency)
C++20-track work reviewed this week included revisions to joining thread, and deprecating volatile
.
v1 of the Concurrency TS will be withdrawn; v2 continues to be under active development, with asymmetric fences approved for it this week.
Executors continue to be a hot topic. SG 1 forwaded two papers related to them onward to the Library Evolution Working Group, while three others remain under review. An earlier plan to ship a subset of executors in C++20 had to be scrapped, because LEWG requested the the “property” mechanism it relies on be generalized, but it was too late in the cycle to progress that for C++20. As a result, Executors are now targeting C++23.
Other proposals under active review in SG 1 concern fibers, concurrent associative data structures, memory model issues, volatile_load
and volatile_store
, customization points for atomic_ref
, and thread-local storage.
SG 7 (Compile-Time Programming)
The main topic in SG 7 continues to be deciding on the high-level direction for constexpr
-based reflection in the (hopefully) C++23 timeframe. The two proposals on the table are scalable reflection in C++ and constexpr reflexpr
; their main point of divergence is whether they use a single type (meta::info
) to represent all compile-time reflection metadata objects (also known as reflections), or whether there should be a family / hierarchy (not necessarily inheritance-based) of such types (meta::variable
, meta::function
, etc.).
SG 7 expressed a preference for the “family of types” approach at the last meeting, however the point continues to be debated as the proposal authors gather more experience. The “single type” approach has been motivated by implementation experience in the EDG and Clang compilers, which has suggested this can achieve better compile-time performance. The “family of types” approach is motivated more by API design considerations, as expressed in the position paper constexpr
C++ is not constexpr
C.
While a consensus on this point is yet to emerge, a possible (and potentially promising) direction might be to build the “family of types” approach as a layer on top of the “single type” approach, which would be the one implemented using compiler primitives.
SG 7 also reviewed a proposal for a modern version of offsetof
, which was forwaded to LEWG.
SG 15 (Tooling)
The Tooling Study Group (SG 15) met for an evening session, primarily to discuss tooling-related concerns around Modules.
As mentioned above, now that Modules has achieved consensus among compiler implemeters, tooling concerns (nicely summarized in this paper) are the remaining significant point of contention.
The concerns fall into two main areas: (1) how build systems interact with Modules, and (2) how non-compiler tools that consume source code (such as static analyzers) can continue to do so in a Modular world. The heart of the issue is that components of the C++ ecosystem that previously needed to rely only on behaviour specified in the C++ standard, and some well-established conventions (e.g. that compilers find included headers using a search path that can be disclosed to tools as well), now in a Modular world need to rely on behaviours that are out of scope of the C++ standard and for which established conventions are yet to emerge (such as how module names are mapped to module interface files, or how translation of imported modules is invoked / performed).
To address these concerns, SG 15 has announced what I view as probably the most exciting development since the group’s inception: that it will aim to produce and publish a C++ Ecosystem Technical Report containing guidance regarding the above-mentioned areas.
A Technical Report (TR) is a type of published ISO document which is not a specification per se, but contains guidance or discussion pertaining to topics covered by other specifications. The committee has previously published a TR in 2006, on C++ performance.
A TR seems like an appropriate vehicle for addressing the tooling-related concerns around Modules. While the committee can’t mandate e.g. how module names should map to file names, by providing guidance about it in the C++ Ecosystem TR, hopefully we can foster the emergence of widely followed conventions and best practices, which can in turn help maintain a high level of interoperability for tools.
The announcement of plans for a C++ Ecosystem TR did not completely assuage tool authors’ concerns; some felt that, while it was a good direction, Modules should be delayed and standardized in tandem with the TR’s publication in the C++23 timeframe. However, this was a minority view, and as mentioned Modules went on to successfully merge into the C++20 working draft at the end of the meeting.
Other Study Groups
Other Study Groups that met at this meeting include:
- SG 6 (Numerics), which met for about two days and reviewed a dozen or so proposals. Topics discussed included utility functions, floating point types, number representations, and linear algebra (the latter being a hot topic for the committee these days, with a lot of interest from the new SG 19 (Machine Learning) as well).
- SG 12 (Undefined and Unspecified Behaviour), which met to discuss an informational paper on pointer provenance and a paper about signed integer overflow; the latter was referred to SG 20 as a matter of educating C++ programmers. There was also the now-usual joint session with WG23 – Software Vulnerabilities, where additional sections of the C++ vulnerabilities document were reviewed; there will also be upcoming work on MISRA.
- SG 13 (Human/Machine Interface), which met for half a day to review a proposal for a standard audio API, which generated a lot of interest. There were no developments related to 2D graphics at this meeting.
- SG 16 (Unicode). Papers reviewed include compile-time regular expressions, source-code information capture, and charset transcoding, transformation, and transliteration.
- SG 19 (Machine Learning) had its first meeting this week. This paper provides a good overview of how SG 19 envisions structuring its work. The initial work is understandably focused on fundamentals such as linear algebra primitives. Like many other study groups, SG 19 will hold monthly teleconferences to make progress in between in-person meetings.
- SG 20 (Education) also had its first in-person meeting. They plan to produce a “standing paper” of educational guidelines (see this proposed early draft). They will also hold monthly telecons.
Next Meetings
The next meeting of the Committee will be in Cologne, Germany, the week of July 15th, 2019.
Conclusion
This was an eventful and productive meeting, and it seems like the progress made at this meeting has been well-received by the user community as well! With Modules and Coroutines joining the ranks of Concepts, Ranges, contracts, default comparisons and much else in the C++20 working draft, C++20 is promising to the most significant language update since C++11.
Due to sheer number of proposals, there is a lot I didn’t cover in this post; if you’re curious about a specific proposal that I didn’t mention, please feel free to ask about it in the comments.
Nice post as always, Botond!
Just to further support your update re TSes: Modules would not have made C++20 without being in a TS first, because a TS let us make progress even though some design questions were still unsettled. For example, language specification wording review in the Core working group probably wouldn’t have started until two years later than it did, and the competing implementations wouldn’t have started converging as soon as they did with work on a “TS mode.” Also, we really did have actual users of the implementations, and they benefited from the implementations’ converging on the TS even partway more than would have happened had we not done a TS, so yes the TS gave us real-world use feedback too including on usability and build throughput.
I love modules, I’m glad they’re in C++20, and so I’m very glad we did the TS which helped them to mature faster than they could have otherwise.
The “proposal for a standard audio API” seems to have the wrong link?
Should it be http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1386r1.pdf ?
Yep. Fixed, thanks!
Thanks for the invaluable information, as usual.