Summary / TL;DR
|Project||What’s in it?||Status|
|C++20||See Reddit report||Technically complete|
|Library Fundamentals TS v3||Library utilities incubating for standardization||Under development|
|Concepts||Constrained templates||Shipping as part of C++20|
|Parallelism TS v2||Task blocks, library vector types and algorithms, and more||Published!|
|Executors||Abstraction for where/how code runs in a concurrent context||Targeting C++23|
|Concurrency TS v2||Concurrency-related infrastructure (e.g. fibers) and data structures||Under active development|
|Networking TS||Sockets library based on Boost.ASIO||Published! Not in C++20.|
|Ranges||Range-based algorithms and views||Shipping as part of C++20|
|Coroutines||Resumable functions (generators, tasks, etc.)||Shipping as part of C++20|
|Modules||A component system to supersede the textual header file inclusion model||Shipping as part of C++20|
|Numbers TS||Various numerical facilities||Under active development|
|C++ Ecosystem TR||Guidance for build systems and other tools for dealing with Modules||Under active development|
|Contracts||Preconditions, postconditions, and assertions||Under active development|
||Under active development|
|Reflection TS||Static code reflection mechanisms||Publication imminent|
|Reflection v2||A value-based
||Under active development|
A few links in this blog post may not resolve until the committee’s post-meeting mailing is published (expected any day). If you encounter such a link, please check back in a few days.
A few weeks ago I attended a meeting of the ISO C++ Standards Committee (also known as WG21) in Prague, Czech Republic. This was the first committee meeting in 2020; you can find my reports on 2019’s meetings here (November 2019, Belfast), here (July 2019, Cologne), and here (February 2019, Kona), and previous ones linked from those. These reports, particularly the Belfast one, provide useful context for this post.
This meeting once again broke attendance records, with about ~250 people present. It also broke the record for the number of national standards bodies being physically represented at a meeting, with reps from Austria and Israel joining us for the first time.
The Prague meeting wrapped up the C++20 standardization cycle as far as technical work is concerned. The highest-priority work item for all relevant subgroups was to continue addressing any remaining comments on the C++20 Committee Draft, a feature-complete C++20 draft that was circulated for feedback in July 2019 and received several hundred comments from national standards bodies (“NB comments”). Many comments had been addressed already at the previous meeting in Belfast, and the committee dealt with the remaining ones at this meeting.
The next step procedurally is for the committee to put out a revised draft called the Draft International Standard (DIS) which includes the resolutions of any NB comments. This draft, which was approved at the end of the meeting, is a technically complete draft of C++20. It will undergo a further ballot by the national bodies, which is widely expected to pass, and the official standard revision will be published by the end of the year. That will make C++20 the third standard revision to ship on time as per the committee’s 3-year release schedule.
I’m happy to report that once again, no major features were pulled from C++20 as part of the comment resolution process, so C++20 will go ahead and ship with all the major features (including modules, concepts, coroutines, and library goodies like ranges, date handling and text formatting) that were present in the Committee Draft. Thanks to this complement of important and long-anticipated features, C++20 is widely viewed by the community as the language’s most significant release since C++11.
Subgroups which had completed processing of NB comments for the week (which was most study groups and the Evolution groups for most of the week) proceeded to process post-C++20 proposals, of which there are plenty in front of the committee.
As with my blog post about the previous meeting, this one will also focus on proceedings in the Evolution Working Group Incubator (EWG-I) which I co-chaired at this meeting (shout-out to my co-chair Erich Keane who was super helpful and helped keep things running smoothly), as well as drawing attention to a few highlights from the Evolution Working Group and the Reflection Study Group. For a more comprehensive list of what features are in C++20, what NB comment resolutions resulted in notable changes to C++20 at this meeting, and which papers each subgroup looked at, I will refer you to the excellent collaborative Reddit trip report that fellow committee members have prepared.
As a reminder, since the past few meetings the committee has been tracking its proposals in GitHub. For convenience, I will also be linking to proposals’ GitHub issues (rather than the papers directly) from this post. I hope as readers you find this useful, as the issues contain useful information about a proposal’s current status; the actual papers are just one further click away. (And shout-out to @m_ou_se for maintaining wg21.link which makes it really easy for me to do this.)
Evolution Working Group Incubator (EWG-I)
EWG-I is a relatively new subgroup whose purpose is to give feedback on and polish proposals that include core language changes — particularly ones that are not in the purview of any of the domain-specific subgroups, such as SG2 (Modules), SG7 (Reflection), etc. — before they proceed to the Evolution Working Group (EWG) for design review.
EWG-I met for three days at this meeting, and reviewed around 22 proposals (all post-C++20 material).
In this section, I’ll go through the proposals that were reviewed, categorized by the review’s outcome.
Forwarded to EWG
The following proposals were considered ready to progress to EWG in their current state:
- A type trait to detect narrowing conversions. This is mainly a library proposal, but core language review was requested to make sure the specification doesn’t paint us into a corner in terms of future changes we might make to the definition of narrowing conversion.
- Guaranteed copy elision for named return objects. This codifies a set of scenarios where all implementations were already eliding a copy, thereby making such code well-formed even for types that are not copyable or movable.
- Freestanding language: optional
::operator new. This is one piece of a larger effort to make some language and library facilities optional in environments that may not be able to support them (e.g. embedded environments or kernel drivers). The paper was favourably reviewed by both EWG-I, and later in the week, by EWG itself.
Forwarded to EWG with modifications
For the following proposals, EWG-I suggested specific revisions, or adding discussion of certain topics, but felt that an additional round of EWG-I review would not be helpful, and the revised paper should go directly to EWG. The revisions requested were typically minor, sometimes as small as adding a feature test macro:
- Language support for class layout control. This introduces a mechanism to control the order in which class data members are laid out in memory. This was previously reviewed by the Refection Study Group which recommended allowing the order to be specified via a library function implemented using reflection facilities. However, as such reflection facilities are still a number of years away, EWG-I felt there was room for a small number of ordering strategies specified in core wording, and forwarded the paper to EWG with one initial strategy, to yield the smallest structure size.
- Object relocation in terms of move and destroy. This aims to address a long-standing performance problem in the language caused by the fact that a move must leave an object in a valid state, and a moved-from object still needs to be destroyed. There is another proposal in this space but EWG-I felt they are different enough that they should advance independently.
- Generalized pack declaration and usage. This proposal significantly enhances the language’s ability to work with variadic parameter packs and tuple-like types. It was reviewed previously by EWG-I, and in this update was reworked to address the feedback from that review. The group felt the proposal was thorough and mature and largely ready to progress to EWG, although there was one outstanding issue of ambiguity that remained to be resolved.
- Types with array-like object representations. This provides a mechanism for enforcing that a structure containing several fields of the same type is laid out in memory exactly the same as a corresponding array type, and the two types can be freely punned.
- C++ identifier syntax using Unicode standard Annex 31. While identifiers in C++ source code can now contain Unicode characters, we do want to maintain some sanity, and so this proposal restricts the set of characters that can appear in identifiers to certain categories (excluding, for example, “invisible” characters).
- Member templates for local classes. Since C++14 introduced generic lambdas (which are syntactic sugar for objects of a local class type defined on the fly, with a templated member call operator), the restriction against explicitly-defined local classes having member templates has been an artificial one, and this proposal lifts it.
- Enable variable template template parameters. Another fairly gratuitous restriction; EWG-I forwarded it, with a suggestion to add additional motivating examples to the paper.
Forwarded to another subgroup
The following proposals were forwarded to a domain-specific subgroup:
- In-source mechanism to identify importable headers. Headers which are sufficiently modular can be imported into a module as if they were modules themselves (this feature is called header units, and is a mechanism for incrementally transitioning large codebases to modules). Such headers are currently identified using some out-of-band mechanism (such as build system metadata). This proposal aims to allow annotating the headers as such in their source itself. EWG-I liked the idea but felt it was in the purview of the Tooling Study Group (SG15).
- Stackable, thread local, signal guards. This aims to bring safer and more modern signal handling facilities to C++. EWG-I reviewed the proposal favourably, and sent it onward to the Library Evolution Incubator and the Concurrency Study Group (the latter saw the proposal later in the week and provided additional technical feedback related to concurrency).
For the following proposals, EWG-I gave the author feedback, but did not consider it ready to forward to another subgroup. A revised proposal would come back to EWG-I.
move = bitcopies. This is other paper in the object relocation space, aiming for a more limited solution which can hopefully gain consensus sooner. The paper was reviewed favourably and will return after revisions.
- Just-in-time compilation. Many attendees indicated this is something they’d find useful in their application domains, and several aspects of the design were discussed. The paper was also seen by the Reflection Study Group earlier in the week.
- Universal template parameters. This allows parameterizing a template over the kind of its template parameters (or, put another way, having template parameters of “wildcard” kind which can match non-type, type, or template template arguments). EWG-I felt the idea was useful but some of the details need to be refined. The proposed syntax is
typename auto Param.
- A pipeline-rewrite operator. This proposes to automatically rewrite
a |> f(b)as
f(a, b), thereby allowing a sequence of compositions of operations to be expressed in a more “linear” way in code (e.g.
x |> f(y) |> g(z)instead of
g(f(x, y), z)). It partly brings to mind previous attempts at a unified function call syntax, but avoids many of the issues with that by using a new syntax rather than trying to make the existing member-call (dot) syntax work this way. Like “spaceship” (
<=>), this new operator ought to have a fun name, so it’s dubbed the “pizza” operator (too bad calling it the “slice” operator would be misleading).
- Partially mutable lambda captures. This proposal seeks to provide finer-grained control over which of a lambda’s captured data members are mutable (currently, they’re all
constby default, or you can make them all mutable by adding a trailing
mutableto the lambda declarator). EWG-I suggested expanding the paper’s approach to allow either
conston any individual capture (the latter useful if combined with a trailing
mutable), as well as to explore other integrations such as a
No consensus in current form
The following proposals had no consensus to continue to progress in their current form. However, a future revision may still be seen by EWG-I if additional motivation is provided or new information comes to light. In some cases, such as with Epochs, there was a strong desire to solve the problems the proposal aims to solve, and proposals taking new approaches to tackling these problems would certainly be welcome.
- Narrowing and widening conversions. This proposal aims to extend the notion of narrowing vs. widening conversions to user-defined conversions, and tweak the overload resolution rules to avoid ambiguity in more cases by preferring widening conversions to narrowing ones (and among widening conversions, prefer the “least widening” one). EWG-I felt that a change as scary as touching the overload resolution rules needed more motivation.
- Improve rules of standard layout. There wasn’t really encouragement of any specific direction, but there was a recognition that “standard layout” serves multiple purposes some of which (e.g. which types are usable in
offsetof) could potentially be split out.
- Epochs: a backward-compatible language evolution mechanism. As at the last meeting, this proposal — inspired heavily by Rust’s editions — attracted the largest crowds and garnered quite a lot of discussion. Overall, the room felt that technical concerns about the handling of templates and the complexity of having to define how features interact across different epochs made the proposal as-is not viable. However, as mentioned, there was strong interest in solving the underlying problems, so I wouldn’t be terribly surprised to see a different formulation of a feature along these lines come back at some point.
- Namespace templates. EWG-I felt the motivation was not sufficiently compelling to justify the technical complexity this proposal would entail.
? :to reduce the scope of
constexpr if. This proposes to allow
? :in type expressions, as in e.g.
using X = cond ? Foo : Bar;. EWG-I didn’t really find the motivation compelling enough to encourage further work on the proposal.
Thoughts on the role of EWG-I
I wrote in my previous post about EWG-I being a fairly permissive group that lets a lot of proposals sail through it. I feel like at this meeting the group was a more effective gatekeeper. However, we did have low attendance at times, which impacted the quantity and quality of feedback that some proposals received. If you’re interested in core language evolution and attend meetings, consider sitting in EWG-I while it’s running — it’s a chance to provide input to proposals at an earlier stage than most other groups!
Here are some highlights of what happened in some of the other subgroups, with a focus on Evolution and Reflection (the rooms I sat in when I wasn’t in EWG-I):
Planning and Organization
As we complete C++20 and look ahead to C++23, the committee has been taking the opportunity to refine its processes, and tackle the next standards cycle with a greater level of planning and organization than ever before. A few papers touched on these topics:
- To boldly suggest an overall plan for C++23. This is a proposal for what major topics the committee should focus on for C++23. A previous version of this paper contained a similar plan for C++20, but one thing that’s new is that the latest version also contains guidance for subgroups for how to prioritize proposals procedurally to achieve the policy objectives laid out in the paper.
- C++ IS Schedule. This formalizes the committee’s 3-year release schedule in paper form, including what milestones we aim for at various parts of the cycle (e.g. a deadline to merge TS’es, a deadline to release a Committee Draft, things like that).
- Direction for ISO C++. Authored by the committee’s Direction Group, this sets out high-level goals and direction for the language, looking forward not just to the next standard release but the language’s longer-term evolution.
- Process proposal: double-check Evolutionary material via a Tentatively Ready status. This is a procedural tweak where proposals approved by Evolution do not proceed to Core for wording review immediately, but rather after one meeting of delay. The intention is to give committee members with an interest in the proposal’s topic but who were perhaps unable to attend its discussions in Evolution (or were unaware of the proposal altogether) — keep in mind, with committee meetings having a growing number of parallel tracks (nine at this meeting), it’s hard to stay on top of everything — to raise objections or chime in with other design-level feedback before the proposal graduates to Core.
The main issue that has precipitated this discussion is the fact that the Library Evolution group has had to reject multiple proposals for improvements to existing library facilities over the past several years, because they would be ABI-breaking, and implementers have been very reluctant to implement ABI-breaking changes (and when they did, like with
std::string in C++11, the C++ ecosystem’s experience with the break hasn’t been great). The paper has a list of such rejected improvements, but one example is not being able to change
unordered_map to take advantage of more efficient hashing algorithms.
The paper argues that rejections of such library improvements demonstrate that the C++ community faces a tradeoff between ABI stability and performance: if we continue to enforce a requirement that C++ standard library facilities remain ABI-compatible with their older versions, as time goes by the performance of these facilities will lag more and more behind the state of the art.
There was a lengthy discussion of this issue, with some polls at the end, which were far from unanimous and in some cases not very conclusive, but the main sentiments were:
- We would like to break the ABI “at some point”, but not “now” (not for C++23, which would be our earliest opportunity). It is unclear what the path is to getting to a place where we would be willing to break the ABI.
- We are more willing to undertake a partial ABI break than a complete one. (In this context, a partial break means some facilities may undergo ABI changes on an as-needed basis, but if you don’t use such facilities at ABI boundaries, you can continue to interlink translation units compiled with the old and new versions. The downside is, if you do use such facilities at ABI boundaries, the consequence is usually runtime misbehaviour. A complete break would mean all attempts to interlink TUs from different versions are prevented at link time.)
- Being ABI-breaking should not cause a library proposal to be automatically rejected. We should consider ABI-breaking changes on a case-by-case basis. Some implementers mentioned there may be opportunities to apply implementation tricks to avoid or mitigate the effects of some breaks.
There was also a suggestion that we could add new language facilities to make it easier to manage the evolution of library facilities — for example, to make it easier to work with two different versions of a class (possibly with different mangled names under the hood) in the same codebase. We may see some proposals along these lines being brought forward in the future.
Reflection and Metaprogramming
The Reflection Study Group (SG7) met for one day. The most contentious item on the agenda concerned exploration of a potential new metaprogramming model inspired by the Circle programming language, but I’ll first mention some of the other papers that were reviewed:
- Just-in-time compilation. This is not really a reflection proposal, but it needs a mechanism to create a descriptor for a template instantiation to JIT-compile at runtime, and there is potential to reuse reflection facilities there. SG7 was in favour of reusing reflection APIs for this rather than creating something new.
- Reflection-based lazy evaluation. This was a discussion paper that demonstrated how reflection facilities could be leveraged in a lazy evaluation feature. While this is not a proposal yet, SG7 did affirm that the group should not rule out the possibility of reflecting on expressions (which is not yet a part of any current reflection proposal), since that can enable interesting use cases like this one.
- Constraint refinement for special-cased functions. This paper aims to fix some issues with parameter constraints, a feature proposed as part of a recent reflection proposal, but the authors withdrew it because they’ve since found a better approach.
- Tweaks to the design of source code fragments. This is another enhancement to the reflection proposal mentioned above, related to source code injection. SG7 encouraged further work in this direction.
? :to reduce the scope of
constexpr if. Like EWG-I, SG7 did not find this proposal to be sufficiently motivating.
- Function parameter constraints are fragile. This paper was mooted by the withdrawal of this one (and of parameter constraints more generally) and was not discussed.
Now onto the Circle discussion. Circle is a set of extensions to C++ that allow for arbitrary code to run at compile time by actually invoking (as opposed to interpreting or emulating) that code at compile time, including having the compiler call into arbitrary third-party libraries.
Circle has come up in the context of considering C++’s approach to metaprogramming going forward. For the past few years, the committee has been trying to make metaprogramming be more accessible by making it more like regular programming, hence the shift from template metaprogramming to
constexpr-based metaprogramming, and the continuing increase of the set of language constructs allowed in
constexpr code (“
constexpr all the things”).
However, this road has not been without bumps. A recent paper argues that
constexpr programming is still quite a bit further from regular programming that we’d like, due to the variety of restrictions on
constexpr code, and the gotchas / limitations of facilities like
std::is_constant_evaluated and promotion of dynamically allocated storage to runtime. The paper argues that if C++ were to adopt Circle’s metaprogramming model, then compile-time code could look exactly the same as runtime code, thereby making it more accessible and facilitating more code reuse.
A response paper analyzes the Circle metaprogramming model and argues that it is not a good fit for C++.
SG7 had an extensive discussion of these two papers. The main concerns that were brought up were the security implications of allowing the compiler to execute arbitrary C++ code at compile time, and the fact that running (as opposed to interpreting) C++ code at compile time presents a challenge for cross-compilation scenarios (where e.g.
sizeof(int) may be different on the host than the target; existing compile-time programming facilities interpret code as if it were running on the target, using the target’s sizes for types and such).
Ultimately, SG7 voted against allowing arbitrary C++ code to run at compile-time, and thus against a wholesale adoption of Circle’s metaprogramming model. It was observed that there may be some aspects of Circle’s model that would still be useful to adopt into C++, such as its model for handling state and side effects, and its syntactic convenience.
Some proposals which went through EWG-I earlier in the week were also reviewed by EWG — in this case, all favourably:
- A type trait to detect narrowing conversions
- Freestanding language: optional
- Guaranteed copy elision for name return objects
Here are some other highlights from EWG this week:
- Pattern matching continues to be under active development and is a priority item for Evolution as per the overall plan. Notable topics of discussion this week included whether pattern matching should be allowed in both statement and expression contexts and, if allowed in expression contexts, whether non-exhaustiveness should be a compile-time error or undefined / other runtime behaviour (and if it’s a compile-time error, in what cases we can expect the compiler to prove exhaustiveness).
*thishas progressed to a stage where the design is pretty mature and EWG asked for an implementation prior to approving it.
auto(x): decay-copy in the language. EWG liked
auto(x), and asked the Library Working Group to give an opinion on how useful it would be inside library implementations. EWG was not convinced of the usefulness of
decltype(auto)(x)as a shorthand for forwarding.
fiber_context– fibers without scheduler. EWG was in favour of having this proposal target a Technical Specification (TS), but wanted the proposal to contain a list of specific questions that they’d like answered through implementation and use experience with the TS. Several candidate items for that list came up during the discussion.
if consteval, a language facility that aims to address some of the gotchas with
std::is_constant_evaluated, will not be pursued in its current form for C++23, but other ideas in this space may be.
- Top-level is constant evaluated is one such other idea, aiming as it does to replace
std::is_constant_evaluatedwith a way to provide two function bodies for a function: a
constexprone and a runtime one. EWG felt this was a less general solution than
if constexprand not worth pursuing.
- A proposal for a new study group for safety-critical applications. EWG encouraged collaboration among people interested in this topic in the wider community. A decision of actually launching a Study Group has been deferred until we see that there is a critical mass of such interest.
=delete‘ing variable templates. EWG encouraged further work on a more general solution that could also apply to other things besides variable templates.
Study Group Highlights
While I wasn’t able to attend other Study Groups besides Reflection, a lot of interesting topics were discussed in other Study Groups. Here is a very brief mention of some highlights:
- In the Concurrency Study Group, Executors have design approval on a long-in-the-works consensus design, and will proceed to wording review at the next meeting, with a view to merging them into the C++23 working draft in the early 2021 timeframe, and thereby unblocking important dependencies like Networking.
- In the Networking Study Group, a proposal to introduce lower-level I/O abstractions that what are currently in the Networking TS was reviewed. Further exploration was encouraged, without blocking the existing Networking TS facilities.
- In the Transactional Memory Study Group, a “Transactional Memory Lite” proposal is being reviewed with an aim to produce a TS based on it.
- The Numerics Study Group is collaborating with the Low Latency and Machine Learning Study Groups on library proposals related to linear algebra and unit systems.
- The Undefined and Unspecified Behaviour Study Group is continuing its collaboration with MISRA and the Programming Language Vulnerabilities Working Group. A revised proposal for a mechanism to mitigate Spectre v1 in C++ code was also reviewed.
- The I/O Study Group reviewed feedback papers concerning audio and graphics. One outcome of the graphics discussion is that the group felt the existing graphics proposal should be more explicit about what use cases are in and out of scope. One interesting observation that was made is that is that learning use cases that just require a simple canvas-like API could be met by using the actual web Canvas API via WebAssembly.
- The Low Latency Study Group reviewed a research paper about low-cost deterministic exceptions in C++, among many other things.
- The Machine Learning Study Group reviewed a proposal for language or library suppport for automatic differentiation.
- The Unicode Study Group decided that
std::regexneeds to be deprecated due to severe performance problems that are unfixable due to ABI constraints.
- The Tooling Study Group reviewed two papers concerning the debuggability of C++20 coroutines, as well as several Modules-related papers. There were also suggestions that the topic of profiling may come up, e.g. in the form of extensions to
std::threadmotivated by profiling.
- The Contracts Study Group reviewed a paper summarizing the issues that were controversial during past discussions (which led to Contracts slipping from C++20), and a paper attempting to clarify the distinction between assertions and assumptions.
The next meeting of the Committee will (probably) be in Varna, Bulgaria, the week of June 1st, 2020.
As always, this was a busy and productive meeting. The headline accomplishment is completing outsanding bugfixes for C++20 and approving the C++20 Draft International Standard, which means C++20 is technically complete and is expected to be officially published by the end of the year. There was also good progress made on post-C++20 material such as pattern matching and reflection, and important discussions about larger directional topics in the community such as ABI stability.
There is a lot I didn’t cover in this post; if you’re curious about something I didn’t mention, please feel free to ask in a comment.
Other Trip Reports
In addition to the collaborative Reddit report which I linked to earlier, here are some other trip reports of the Prague meeting that you could check out: