2021 C++ Standardization Highlights

Introduction

The ISO C++ Standards Committee (also known as WG21) has not met in person since its February 2020 meeting in Prague, which I wrote about here.

However, the committee and its subgroups have continued their work through remote collaboration, and a number of notable proposals have been adopted into C++23, the next language version, in this way, with many others in the pipeline.

In this post, I will outline some of the highlights of the committee’s work in 2021. (The post will also cover some material from the latter part of 2020, a period when remote collaboration was already underway but which I have not covered in any previous post.) I’ve been less involved in the committee than before, so this post will not be as comprehensive as my previous trip reports, but I hope to share the proposals I’ve found most notable.

Collaborating Remotely

The committee consists of four Working Groups and 22 Study Groups (a subset of which are active at any given time). The in-person meeting schedule was three 6-day meetings per year; other than a plenary session at the beginning at the end, most of the week would be spent with the subgroups meeting in parallel (not all of them necessarily for the whole week).

To continue evolving C++ remotely, subgroups began to meet remotely via Zoom teleconferences. (For some subgroups, this was more a continuation of a practice they had already adopted to hold telecons in between meetings.) Subgroups would meet independently, at a frequency determined by their respective chairs. In addition, virtual plenary telecons to hold committee-wide votes continued to be held three times per year.

Remote collaboration has a number of challenges, such as timezone differences, and the lower fidelity communication signal of video conferences over physically being in the same room. It can also be more challenging to make time for meetings that recur regularly over the course of a year (for example, 90 minutes every week, as is the case for the Evolution Working Group telecons) than to set a week aside for committe work. At the same time, remote collaboration has the upside of making the committee’s work more accessible, allowing experts to contribute from around the world without making the time or cost investment of physically travelling to a meeting location that may be on a different continent for them.

One key to successful remote collaboration is being well-organized, including documenting your processes in writing. I think the committee has done pretty well in that regard, making a lot of implicit process knowledge explicit in recent years. Some key process documents include:

  • The C++ International Standard schedule, which lays out the committee’s intention to put out a new standard version every 3 years, and the key deadlines that need to be met for various milestones leading up to a standard release. The committee has retained this 3-year train model notwithstanding the transition to remote collaboration; while the amount of material that makes it into C++23 may be reduced as a result of pandemic disruptions, the C++23 train will leave on time.
  • An outline of the committee’s top-level priorities for the C++23 cycle
  • An outline of the committee’s remote collaboration model
  • Additional documentation of how proposals move between subgroups

Some key resources for keeping track of the committee’s work:

  • A list of proposals submitted to the committee can be found here. They are organized into “mailings”, which are now posted monthly to facilitate faster collaboration.
  • The status of each proposal can be found in the committee’s github issue tracker. Every paper has an issue (which can be found by searching for the paper number, or by using the forwarding link https://wg21.link/pXXXX/github), which documents which subgroups have seen the paper and the outcomes of any polls that were taken.

In the rest of this post, when referring to a proposal, I’m going to link to its github issue, so that it’s easy to check on its precise status. The proposal itself is linked from the issue (scroll down for later revisions).

C++23

Here is a selection of proposals that have been voted into C++23 working draft:

Language

  • Deducing this, which allows making the object (this) parameter of member functions explicit, and making its type templated and deduced. This versatile language change enables a number of interesting use cases:
    • Avoiding code duplication when the object parameter has different constness or reference qualification. Previously, separate overloads would be needed to cover these cases.
    • A design pattern for static polymorphism that’s considerably simplified compared to the Curiously Recurring Template Pattern (CRTP).
    • Member functions that take their object parameter by value. (Compare Rust methods that take self by value, consuming the object when called.)
    • Recursive lambdas, made possible by the fact that the explicit object parameter of the lambda’s call operator gives you a way to refer to the previously-unnamed lambda for recursive calls.

    See the examples section of the paper for illustrations of these and more.

  • Multidimensional subscript operator. This allows the [] operator to take multiple arguments (e.g. matrix[i, j]), which improves the interfaces of multidimensional data structures like matrices. Previously, you’d have to use chained calls like matrix[i][j] (which requires a temporary proxy object to represent the intermediate result matrix[i]), or use the () operator instead.
  • if consteval, which gives you a reliable way to check if a function is being called during constant evaluation or during runtime, and do something different in each case.
  • Decay-copy in the language. This adds a new expression, auto(x), which makes a copy of x following decay rules (for example, array-to-pointer decay) much as if x were assigned to a new variable (e.g. auto y = x;), but without introducing a new lvalue to refer to the copy.
  • A literal suffix for size_t. The suffix is uz, to be consistent with the existing convention that suffixes for unsigned integer types include u. The suffix z is also introduced (again for consistency) to represent the signed integer type corresponding to size_t, though in practice this is not likely to be needed often.
  • Narrowing contextual conversions to bool. This allows narrowing conversions to bool in some cases where it’s clear that a boolean result is desired, such as in if (flags & Flag).
  • #elifdef and #elifndef. This fixes a small consistency gap in the preprocessor which had #ifdef as a shorthand for #if defined(...) but no corresponding shorthand for #elif. The new directives were added to C as well.
  • Mandating declaration-order class layout. This mandates that data members of a class must be laid out in memory in order of their declaration. Previously, compilers had the freedom to reorder sections of the class with different access control, though no implementation was known to make use of this, so this paper is standardizing existing practice.

Library

Technical Specifications (TS)

In addition to the C++ International Standard, the following Technical Specifications (sets of features specified independently to gather implementation and use experience and consider for inclusion into a future International Standard) are under development or ballot:

  • A Minimal Transactional Memory TS (formerly called “Transactional Memory Lite”). This is a simplification of the Transactional Memory TS published in 2015, with the goal of making conforming implementations easier to produce, and thus increasing the chances of collecting implementation and use experience and proceeding with standardization. This has been approved to be sent out for a DTS (Draft Technical Specification) ballot, where national standards bodies can provide formal comments prior to final publication.
  • Version 2 of the Concurrency TS, containing facilities for implementing the hazard pointer and read-copy-update techniques.

Evolution Working Group (EWG)

In these sections, I’ll highlight some proposals that have not yet made it into the working draft but are still cooking in the language and library Evolution Working Groups.

These proposals have been approved in EWG telecons. Their next stop per the new collaboration process is an EWG electronic poll, followed by wording review and finally a plenary poll; assuming these steps go well, they have a good chance of making C++23:

  • Portable assumptions, which allows expressing instructions for the compiler to assume for optimization purposes that certain expressions are true (currently accomplished using compiler-specific builtins such as __builtin_assume) in a portable way. In the C++20 cycle this use case was going to be subsumed by contracts, but since contracts are not on track to appear in C++23, EWG has now gone back to addressing this use case independently.
  • static operator(), which allows for a more efficient representation of stateless callables
  • constexpr class, a shorthand for declaring a class whose member functions are all constexpr
  • A type trait to detect reference binding to a temporary

These proposals are still under discussion, in most cases waiting for a revision from the proposal authors. Given where we are in the cycle, these are less likely to make C++23:

Fixing the Range-Based For Loop

The range-based for loop suffers from a tricky lifetime issue, where the way it’s specified using rewrite rules means that the result of the range expression itself gets its lifetime extended (if it’s a temporary) for the duration of the loop, but this does not apply to sub-expressions of the range expression.

This makes loops like for (auto& item : foo(temp())), where temp() returns a temporary object and foo() returns a reference into that object (rather than, say, taking ownership of it), exhibit undefined behaviour, because the temporary does not remain alive for the loop body. This contrasts from the same construct written using an algorithm like std::for_each(), where the endpoint for temporary lifetimes is the entire function call which includes the body of the loop in the implementation.

This has been a persistent gotcha, and it has limited the design of Range adaptors (where e.g. get_vector() | transformed(f) is currently not allowed because otherwise it would be susceptible to this issue).

It was encouraging, therefore, to see a proposal to fix this long-standing issue, by adjusting the lifetime extension rules to apply to all subexpressions of the range expression in this case.

Unfortunately, the proposal narrowly failed to garner consensus in EWG, with some participants feeling that this was too specialized a solution that would not address similar cases of dangling references in contexts other than the range-based for loop, such as auto&& range = foo(temp()); /* work with range */. I think the counterargument here is that in these cases, the dangling reference is explicitly visible, whereas in the range-based for loop it’s hidden inside the implicit rewrite of the loop.

Named Arguments Making a Comeback?

While it has not yet been officially submitted as a P-numbered paper, nor has it been reviewed by EWG, I’ve come across a draft proposal for named arguments (in this formulation called designated arguments) which was circulated on the committee mailing lists (including the public std-discussion list); there’s also an accompanying video explainer.

This looks to me like a thorough, well-researched proposal which addresses the concerns raised during discussions of previous proposals for named arguments (including one Ehsan Akhgari and I co-authored), most notably by making the declaration syntax opt-in and the argument names encoded in the function type.

I look forward to seeing this presented to EWG in the future.

Library Evolution Working Group (LEWG)

LEWG has identified the following areas as priorities to focus on during the C++23 cycle. Note that not all of these may ultimately make it into C++23; executors, networking, and coroutine support are particularly at risk.

Additionally, std::expected, a type representing a value or an error (similar to Rust’s Result) has been approved by LEWG for C++23 and is currently awaiting wording review.

Here are some other proposals that LEWG has reviewed but which are not headed for C++23:

The Future of Library Technical Specifications

One of the recent LEWG reports contained a section discussing the future of library Technical Specifications, which describes a shift away from TSes in favour of putting library facilities directly into the next International Standard. A key part of the justification is:

Technical Specifications provide implementation experience, but they do not deliver the levels of usage and deployment experience, or user feedback, that we had wished for.

This matches a similar observation I made about language Technical Specifications in a previous trip report.

Study Groups

The committee has at least 18 active study groups (see list here). I haven’t been following all of their work in detail, but I’ll call out a couple:

Reflection Study Group

Progress on Reflection continues to be slow, with no proposal ready for standardization in the C++23 time frame.

Interestingly, while recent proposals (like this one) have focused on constexpr, value-based interfaces, there has also been recent implementation progress on the older, template metaprogramming based Reflection TS. Matúš Chochlík, the author of the original reflection proposal that substantially shaped the Reflection TS, has implemented the Reflection TS in a fork of clang (also available on Compiler Explorer).

I view this as an exciting development because it opens the door to gathering concrete usage experience with reflection facilities; even if that experience is using a different syntax than what we’d ultimately like to standardize, it’s likely to produce valuable and actionable feedback that can inform the standardization process going forward.

Tooling Study Group

One of the areas of focus of the Tooling Study Group remains promoting interoperability in the ecosystem surrounding C++20 Modules usage and distrbution. Notable proposals in this area include a proposal for a format for describing the dependencies of source files, and a proposal for a format for describing information to facilitate the consumption of pre-built module artifacts.

Future Plans

The committee is starting to wrap up work on new C++23 features, with the deadline for design approval of new features being February 2022, and the deadline for the C++23 draft wording to be feature complete (and sent out for its first, Committee Draft ballot) being July 2022.

Collaboration for the time being continues to be remote. As of this writing, the earliest in-person meeting not to be definitively cancelled is the one in July 2022; it remains to be seen whether we will in fact be able to hold this meeting in person.

Whenever the committee does resume in person meetings, they’re likely to (at least initially) be hybrid, meaning there will be A/V equipment to allow continued remote participation for those who prefer it.

I know there are many subgroups and topics I haven’t covered; if you’re interested in one in particular, please feel free to ask about it in a comment.

Desktop pinch-zoom support arrives with Firefox 83

Today is the release date of Firefox 83. One of the new features in this release is support for pinch-zooming on desktop platforms, a feature whose development I was involved with and which I’ll describe briefly in this blog post.

Pinch gestures have long been the standard method of interaction to trigger zooming on mobile devices, and the mobile version of Firefox has supported them since (I believe) its inception.

The desktop version of Firefox has also had a zoom feature for a long time, activated via Ctrl+Plus/Minus or Ctrl+mousewheel or a button in the UI, but importantly, this performs a different type of zooming than pinch gestures on mobile, as I’ll illustrate.

The conventional method of zooming on desktop performs reflowing zoom, which looks like this:

By contrast, pinch gestures on mobile perform non-reflowing zoom, which looks like this:

While both methods of zooming increase the scale of text, images, and other content elements, reflowing zoom reflows the page such that the scaled-up content still fits into the same horizontal viewport width. This can cause text to be split up into lines differently, and more generally elements to change their position relative to each other. Non-reflowing zoom leaves the page layout alone and just scales the content, creating overflow in the horizontal direction that you need to scroll to bring into view.

Non-reflowing zoom has some important advantages over reflowing zoom:

  • Reflow is expensive, making non-reflowing zoom much faster and more responsive.
  • Non-reflowing zoom is more predictable in the sense that the content at the pinch gesture’s “focus point” will remain stationary, so you can zoom in “on an element” more accurately.

The non-reflowing behaviour can also be a downside: if it’s a paragraph of text you’re zooming in on, if you zoom to a point where the lines are wider than the screen, you have to scroll horizontally to read a whole line.

Basically, reflowing and non-reflowing zoom lend themselves to different use cases. Reflowing zoom is useful if, say, you’re reading an article but the text is a bit small for comfort (though I’ll take the opportunity to plug Firefox’s Reader View as an even better option for this case). Non-reflowing zoom is useful if, say, you want to zoom in on an image or diagram to get a closer look at it.

Firefox users have long asked for the ability to trigger non-reflowing zoom on desktop platforms, using pinch gestures on a touchscreen or touchpad, a feature that other browsers have introduced over time as well. Firefox 83 finally fulfils this request, for touchscreens on all platforms, and touchpads on Windows and Mac. (Linux touchpads are still a work in progress.)

Reflowing zoom hasn’t gone away, it’s still activated using the same means as before (Ctrl+mousewheel etc.).

I’d like to extend a big thank you to my colleagues Kartikaya Gupta, Timothy Nikkel, and Kris Taeleman, with whom I collaborated on this feature, among many others who lent a helping hand.

Implementing this feature was trickier than it sounds. Without getting into too many details, some of the main sources of complexity were:

  • Scrollbars, particularly given that on desktop platforms you can interact with them (i.e. click them / drag them). I think our implementation here ended up being more complete than Chrome’s, in that scrollbars remain fully interactive even when zoomed.
  • Web pages that want to handle pinch gestures themselves and “do their own thing”, such as Google Maps. Careful coordination is required to determine whether the pinch gesture should be handled by the page, or by the browser.

If you’re a Firefox user with a touchscreen or a touchpad, I encourage you to give the new zoom feature a try! If you’re not a Firefox user, I encourage you to try Firefox 🙂 If you run into any issues, bug reports are appreciated.

Trip Report: C++ Standards Meeting in Prague, February 2020

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
Pattern matching A match-like facility for C++ Under active development
Reflection TS Static code reflection mechanisms Publication imminent
Reflection v2 A value-based constexpr formulation of the Reflection TS facilities, along with more advanced features such as code injection 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.

Introduction

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).

Feedback given

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 const by default, or you can make them all mutable by adding a trailing mutable to the lambda declarator). EWG-I suggested expanding the paper’s approach to allow either mutable or const on any individual capture (the latter useful if combined with a trailing mutable), as well as to explore other integrations such as a mutable capture-default.

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.
  • Using ? : 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!

Other Highlights

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.

ABI Stability

In one of the week’s most notable (and talked-about) sessions, Evolution and Library Evolution met jointly to discuss a paper about C++’s approach to ABI stability going forward.

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.
  • Using ? : 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.

Evolution Highlights

Some proposals which went through EWG-I earlier in the week were also reviewed by EWG — in this case, all favourably:

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).
  • Deducing *this has 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_evaluated with a way to provide two function bodies for a function: a constexpr one and a runtime one. EWG felt this was a less general solution than if constexpr and 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::regex needs 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::thread motivated 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.

Next Meeting

The next meeting of the Committee will (probably) be in Varna, Bulgaria, the week of June 1st, 2020.

Conclusion

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:

Developing Mozilla C++ code with clangd and VSCode

Introduction

I’ve long been a fan of smart editors which have a semantic understanding of the code you’re editing, and leverage it to provide semantics-aware features such as accurate code completion (only offering completions for names that are actually in scope), go-to-definition, find references, semantic highlighting, and others.

When I joined Mozilla six years ago, my choice of editor for C++ code was Eclipse CDT, because based on experience and research, this was the most fully-featured option that was cross-platform and open-source. (Depending on who you ask, Visual Studio, XCode, and CLion have, at various times, been described as matching or exceeding Eclipse CDT in terms of editor capabilities, but the first two of these are single-platform tools, and are three all proprietary.)

This assessment was probably accurate at that time, and probably even for much of the intervening time, but in recent years Eclipse CDT has not aged well. The main reason for this is that Eclipse CDT has its own C++ parser. (For brevity, I’m using “parsing” here as an umbrella term for lexing, preprocessing, parsing, semantic analysis, and all other tasks that need to be performed to build a semantic model of code from source.) C++ is a very complex language to parse, and thus a C++ parser requires a lot of effort to write and maintain. In the early days of CDT, there was a lot of investment, mostly from commercial vendors that packaged CDT-based IDEs, in building and maintaining CDT’s parser, but over time, the level of investment has faded. Meanwhile, the C++ language has been gaining new features at an increasing rate (and the Mozilla codebase adopting them — we’re on the verge of switching to C++17), and CDT’s parser just hasn’t been able to keep up.

Trends in editor tooling have also evolved since CDT’s heyday. Two major themes, in particular, have emerged:

  • Building tooling on top of compiler internals. Since C++ parsers are so expensive to write and maintain, people don’t tend to write new ones these days. Rather, new tooling tends to be built on top of the internals of C++ compilers. The emergence of clang as a modular C++ compiler that was written with tooling uses in mind, has been instrumental to this shift.
  • Decoupling language intelligence from editor front-ends. People’s tastes in editors vary of lot, with some swearing by command-line editors like vim or emacs, and others preferring IDEs like Eclipse or Visual Studio. However, the semantic capabilities that people want (e.g. go-to-definition) are largely the same. So, it makes sense to decouple these, and separate the “language intelligence” that powers semantic features from the editors themselves. Microsoft has been spearheading an effort in this area, called the Language Server Protocol (LSP), which defines an interface between editor front-ends (“language clients”) and components which provide language intelligence (“language servers”).

In accordance with these trends, the new hotness in C++ editor tooling is leveraging the clang compiler’s front-end, and more specifically LSP-based C++ language servers. A number of clang-based C++ language server projects have sprung up over the past couple of years, but in recent months, clangd (short for “clang daemon”) seems to have emerged as the C++ community’s choice. (Among other advantages, clangd lives in-tree in the LLVM codebase, so it gets to use clang’s rich but unstable C++ API without having to constantly deal with API breakages.)

In light of these developments, I’ve been looking at switching my development workflows from using Eclipse CDT, to an editor that uses clangd. To this end, I’ve been following the development of clangd, contributing to it, and trying it out, and I’m happy to report that it has reached a state where I’m productively using as a replacement for CDT. I wouldn’t quite say it has reached feature parity with CDT yet — but I think the respects in which it’s better (such as full C++17 support, and much faster code completion) clearly outweigh the remaining feature gaps.

In the remainder of this article, I will describe how to set up a Mozilla C++ development environment using clangd. As my client, I use Visual Studio Code (VSCode), which is the most mature LSP client out there (and Microsoft’s reference LSP client), but it should be possible to use clangd with any other editor that has LSP client capabilities — including vim, emacs, and Sublime Text (with some caveats, e.g. semantic highlighting is currently a clangd extension rather than an LSP feature proper, so it’s only available in the vscode-clangd client).

Setting up clangd + VSCode for Mozilla development

Install clangd

As clangd is still under active development and bugs are being fixed and improvements made on a daily basis, I recommend using the latest nightly version. For Debian-based Linux distributions, the easiest way to get these is via apt.llvm.org, using the “development branch” packages. As of LLVM 10, clangd now has its own dedicated package (named clangd, possibly with a version suffix like clangd-10); in earlier versions it was in the clang-tools package.

If you’re not inclined to use a nightly build, the next best option is the latest release, which at the time of this writing is clangd-9.

Important note: using clangd nightly does not mean you have to build your code with a corresponding clang nightly. You can continue building your code with any older version of clang, or even with gcc.

After installation, clangd does not require any further setup beyond ensuring that the clangd binary is in your PATH (which with a .deb package should happen by default).

Install VSCode

VSCode can be installed following instructions on its website; it, too, has Linux packages available (both .deb and .rpm). Note that, unlike Visual Studio, VSCode is both cross-platform and free/open-source software.

In addition to VSCode itself, you’ll want to install the vscode-clangd plugin. This plugin tells VSCode to use clangd as the language server for C++, and also implements some LSP extensions that clangd supports, like semantic highlighting.

Note: when you first open a .cpp file in VSCode, it will likely recommend installing Microsoft’s proprietary vscode-cpptools plugin. Do not install this if you plan to use vscode-clangd (or disable it if it’s already installed); if both enabled, the two plugins will step on each other’s toes.

Project setup

The last step is to tell clangd about Mozilla’s project configuration, so it can parse our C++ code properly (with the proper include paths, defines, and other flags). Thankfully, this is fairly straightforward, due to the fact that clangd’s primary means of consuming project configuration info is via a compilation database, and our build system has built-in support for generating one of those.

These are the steps I followed:

  1. If bug 1583999 isn’t fixed yet, apply my patch for it (attached to the bug) locally. This disables unified builds for the purposes of generating a compilation database (only; the actual build will still be unified), because clangd has trouble understanding unified builds.
  2. In your srcdir, run ./mach build-backend -b CompileDB. This will generate a compilation database (compile_commands.json file) in the objdir.
  3. Create .vscode/settings.json in your srcdir, with the following contents:
    {
      "clangd.arguments": [
        "--compile-commands-dir",
        "<path-to-objdir>"
      ],
    }  
        

    This tells clang where to find the compilation database for this workspace.

You’re now ready to launch VSCode, either from the command line via code <srcdir>, or by opening the srcdir as the project folder from the VSCode menu.

At this point, clangd will begin indexing the workspace. Indexing all of mozilla-central can take an hour or more (you can see its progress if you open the Output view and select “Clangd Language Server” from the dropdown). However — importantly — TU-local features like completion, go-to-definition, and semantic highlighting do not use the index, and are available from the start (within a few seconds of opening a .cpp file, which is how long it takes for clangd to build an AST for it). Only cross-TU features like “find references” require the index, so those will become available when indexing is complete. (This is a notable improvement over Eclipse CDT, which required the project to be indexed for almost all semantic features.)

One thing to note is that because we are disabling unified builds for the purpose of generating the compilation database, and because we no longer test non-unified builds in CI, clangd may occasionally show errors that only occur in non-unified builds. These are typically caused by vioaltions of the “include what you use” principle; if you come across them, I suggest taking the opportunity to fix them 🙂

If you’re looking to improve your C++ editor experience, I encourage you to give this a try! You can report any issues you encounter in the clangd issue tracker.

Trip Report: C++ Standards Meeting in Belfast, November 2019

Summary / TL;DR

Project What’s in it? Status
C++20 See below On track
Library Fundamentals TS v3 See below Under development
Concepts Constrained templates In 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 See below Under active development
Networking TS Sockets library based on Boost.ASIO Published! Not in C++20.
Ranges Range-based algorithms and views In C++20
Coroutines Resumable functions (generators, tasks, etc.) In C++20
Modules A component system to supersede the textual header file inclusion model In C++20
Numerics 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
Pattern matching A match-like facility for C++ Under active development
Reflection TS Static code reflection mechanisms Publication imminent
Reflection v2 A value-based constexpr formulation of the Reflection TS facilities Under active development
Metaclasses Next-generation reflection facilities Early development

A few links in this blog post may not resolve until the committee’s post-meeting mailing is published (expected within a few days of November 25, 2019). If you encounter such a link, please check back in a few days.

Introduction

Last week I attended a meeting of the ISO C++ Standards Committee (also known as WG21) in Belfast, Northern Ireland. This was the third and last committee meeting in 2019; you can find my reports on preceding meetings here (July 2019, Cologne) and here (February 2019, Kona), and previous ones linked from those. These reports, particularly the Cologne one, provide useful context for this post.

At the last meeting, the committee approved and published the C++20 Committee Draft (CD), a feature-complete draft of the C++20 standard which includes wording for all of the new features we plan to ship in C++20. The CD was then sent out to national standards bodies for a formal ISO ballot, where they have the opportunity to file technical comments on it, called “NB (national body) comments”.

We have 10-15 national standards bodies actively participating in C++ standardization, and together they have filed several hundred comments on the CD. This meeting in Belfast was the first of two ballot resolution meetings, where the committee processes the NB comments and approves any changes to the C++20 working draft needed to address them. At the end of the next meeting, a revised draft will be published as a Draft International Standard (DIS), which will likely be the final draft of C++20.

NB comments typically ask for bug and consistency fixes related to new features added to C++20. Some of them ask for fixes to longer-standing bugs and consistency issues, and some for editorial changes such as fixes to illustrative examples. Importantly, they cannot ask for new features to be added (or at least, such comments are summarily rejected, though the boundary between bug fix and feature can sometimes be blurry).

Occasionally, NB comments ask for a newly added feature to be pulled from the working draft due to it not being ready. In this case, there were comments requesting that Modules and Coroutines (among other things) be postponed to C++23 so they can be better-baked. I’m pleased to report that no major features were pulled from C++20 at this meeting. In cases where there were specific technical issues with a feature, we worked hard to address them. In cases of general “this is not baked yet” comments, we did discuss each one (at length in some cases), but ultimately decided that waiting another 3 years was unlikely to be a net win for the community.

Altogether, over half of the NB comments have been addressed at this meeting, putting us on track to finish addressing all of them by the end of the next meeting, as per our standardization schedule.

While C++20 NB comments were prioritized above all else, some subgroups did have time to process C++23 proposals as well. No proposals were merged into the C++23 working draft at this time (in fact, a “C++23 working draft” doesn’t exist yet; it will be forked from C++20 after the C++20 DIS is published at the end of the next meeting).

Procedural Updates

A few updates to the committee’s structure and how it operates:

  • As the Networking TS prepares to be merged into C++23, it has been attracting more attention, and the committee has been receiving more networking-related proposals (notable among them, one requesting that networking facilities be secure by default), so the Networking Study Group (SG4) has been re-activated so that a dedicated group can give these proposals the attention they deserve.
  • An ABI Review Group (ARG) was formed, comprised of implementors with ABI-related expertise on various different platforms, to advise the committee about the ABI impacts of proposed changes. The role of this group is not to set policy (such as to what extent we are willing to break ABI compatibility), but rather to make objective assessments of ABI impact on various platforms, which other groups can then factor into their decision-making.
  • Not something new, just a reminder: the committee now tracks its proposals 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.
  • At this meeting, GitHub was also used to track NB comments, one issue per comment, and you can also see their status and resolution (if any) there.

Notes on this blog post

This blog post will be a bit different from previous ones. I was asked to chair the Evolution Working Group Incubator (EWG-I) at this meeting, which meant that (1) I was not in the Evolution Working Group (EWG) for most of the week, and thus cannot report on EWG proceedings in as much detail as before; and (2) the meeting and the surrounding time has been busier for me than usual, leaving less time for blog post writing.

As a result, in this blog post, I’ll mostly stick to summarizing what happened in EWG-I, and then briefly mention a few highlights from other groups. 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.

Evolution Working Group Incubator (EWG-I)

EWG-I (pronounced “oogie” by popular consensus) is a relatively new subgroup, formed about a year ago, 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 EWG for design review.

EWG-I met for two and a half days at this meeting, and reviewed 17 proposals. All of this was post-C++20 material.

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:

  • Narrowing contextual conversions to bool. This proposal relaxes a recently added restriction which requires an explicit conversion from integer types to bool in certain contexts. The motivation for the restriction was noexcept(), to remedy the fact that it was very easy to accidentally declare a function as noexcept(f()) (which means “the function is noexcept if f() returns a nonzero value”) instead of noexcept(noexcept(f())) (which means “the function is noexcept if f() doesn’t throw”), and this part of the restriction was kept. However, the proposal argued there was no need for the restriction to also cover static_assert and if constexpr.
  • Structured bindings can introduce a pack. This allows a structured binding declaration to introduce a pack, e.g. auto [...x] = f();, where f() is a function that returns a tuple or other decomposable object, and x is a newly introduced pack of bindings, one for each component of the object; the pack can then be expanded as x... just like a function parameter pack.
  • Reserving attribute names for future use. This reserves attribute names in the global attribute namespace, as well as the attribute namespace std (or std followed by a number) for future standardization.
  • Accessing object representations. This fixes a defect introduced in C++20 that makes it undefined behaviour to access the bytes making up an object (its “object representation”) by reinterpret_casting its address to char*.
  • move = relocates. This introduces “relocating move constructors”, which are move constructors declared using = relocates in places of = default. This generates the same implementation as for a defaulted move constructor, but the programmer additionally guarantees to the compiler that it can safely optimize a move + destructing the old object, into a memcpy of the bytes into the new location, followed by a memcpy of the bytes of a default-constructed instance of the type into the old location. This essentially allows compilers to optimize moves of many types (such as std::shared_ptr), as well as of arrays / vectors of such types, into memcpys. Currently, only types which have an explicit = relocates move constructor declaration are considered relocatable in this way, but the proposal is compatible with future directions where the relocatability of a type is inferred from that of its members (such as in this related proposal).

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:

  • fiber_context – fibers without scheduler. This is the current formulation of “stackful coroutines”, or rather a primitive on top of which stackful coroutines and other related things like fibers can be built. It was seen by EWG-I so that we can brainstorm possible interactions with other language features. TLS came up, as discussed in more detail in the EWG section.
  • Making operator?: overloadable. See the paper for motivations, which include SIMD blend operations and expression template libraries. The biggest sticking point here is we don’t yet have a language mechanism for making the operands lazily evaluated, the way the built-in operator behaves. However, not all use cases want lazy evaluation; moreover, the logical operators (&& and ||) already have this problem. EWG-I considered several mitigations for this, but ultimately decided to prefer an unrestricted ability to overload this operator, relying on library authors to choose wisely whether or not to overload it.
  • Make declaration order layout mandated. This is largely standardizing existing practice: compilers technically have the freedom to reorder class fields with differing access specifiers, but none are known to do so, and this is blocking future evolution paths for greater control over how fields are laid out in memory. The “modification” requested here is simply to catalogue the implementations that have been surveyed for this.
  • Portable optimisation hints. This standardizes __builtin_assume() and similar facilities for giving the compiler a hint it can use for optimization purposes. EWG-I expressed a preference for an attribute-based ([[assume(expr)]]) syntax.

Note that almost all of the proposals that were forwarded to EWG have been seen by EWG-I at a previous meeting, sometimes on multiple occasions, and revised since then. It’s rare for EWG-I to forward an initial draft (“R0”) of a proposal; after all, its job is to polish proposals and save time in EWG as a result.

Forwarded to another subgroup

The following proposals were forwarded to a domain-specific subgroup:

  • PFA: a generic, extendable and efficient solution for polymorphic programming. This proposed a mechanism for generalized type erasure, so that types like std::function (which a type-erased wrapper for callable objects) can easily be built for any interface. EWG-I forwarded this to the Reflection Study Group (SG7) because the primary core language facility involves synthesizing a new type (the proxy / wrapper) based on an existing one (the interface). EWG-I also recommended expressing the interface as a regular type, rather than introducing a new facade entity to the language.

Feedback given

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.

No proposals were outright rejected at this meeting, but the nature of the feedback did vary widely, from requesting minor tweaks, to suggesting a completely different approach to solving the problem.

  • Provided operator= returns lvalue-ref on an rvalue. This attempts to rectify a long-standing inconsistency in the language, where operator= for a class type can be called on temporaries, which is not allowed for built-in types; this can lead to accidental dangling. EWG-I agreed that it would be nice to resolve this, but asked the author to assess how much code this would break, so we can reason about its feasibility.
  • Dependent static assertion. The problem this tries to solve is that static_assert(false) in a dependent context fails eagerly, rather than being delayed until instantiation. The proposal introduces a new syntax, static_assert<T>(false), where T is some template parameter that’s in scope, for the delayed behaviour. EWG-I liked the goal, but not the syntax. Other approaches were discussed as well (such as making static_assert(false) itself have the delayed behaviour, or introducing a static_fail() operator), but did not have consensus.
  • Generalized pack declaration and usage. This is an ambitious proposal to make working with packs and pack-like types much easier in the language; it would allow drastically simplifying the implementations of types like tuple and variant, as well as making many compile-time programming tasks much easier. A lot of the feedback concerned whether packs should become first-class language entities (a “language tuple” of sorts, as previously proposed), or remain closer to their current role as depenent constructs that only become language entities after expansion.
  • Just-in-time compilation. Another ambitious proposal, this takes aim at use cases where static polymorphism (i.e. use of templates) is desired for performance, but the parameters (e.g. the dimensions of a matrix) are not known at compile time. Rather than being a general-purpose JIT or eval() like mechanism, the proposal aims to focus on the ability to instantiate some templates at runtime. EWG-I gave feedback related to syntax, error handling, restricting runtime parameters to non-types, and consulting the Reflection Study Group.
  • Interconvertible object representations. This proposes a facility to assert, at compile time, than one type (e.g. a struct containing two floats) has the same representation in memory as another (e.g. an array of two floats). EWG-I felt it would be more useful it the proposed annotation would actually cause the compiler to use the target layout.
  • Language support for class layout control. This aims to allow the order in which class members are laid out in memory to be customized. It was reviewed by SG7 (Reflection) as well, which expressed a preference for performing the customization in library code using reflection facilities, rather than having a set of built-in layout strategies defined by the core language. EWG-I preferred a keyword-based annotation syntax over attributes, though metaclasses might obviate the need for a dedicated syntax.
  • Epochs: a backward-compatible language evolution mechanism. This was probably the most ambitious proposal EWG-I looked at, and definitely the one that attracted the largest audience. It proposes a mechanism similar to Rust’s editions for evolving the language in ways we have not been able to so far. Central to the proposal is the ability to combine different modules which use different epochs in the same program. This generated a lot of discussion around the potential for fracturing the language into dialects, the granularity at which code opts into an epoch, and what sorts of new features should be allowed in older epochs, among other topics.

Thoughts on the role of EWG-I

Having spent time in both EWG and EWG-I, one difference that’s apparent is that EWG is the tougher crowd: features that make it successfully through EWG-I are often still shot down in EWG, sometimes on their first presentation. If EWG-I’s role is to act as a filter for EWG, it is effective in that role already, but there is probably potential for it to be more effective.

One dynamic that you often see play out in the committee is the interplay between “user enthusiasm” and “implementer skepticism”: users are typically enthusiastic about new features, while implementers will often try to curb enthusiasm and be more realistic about a feature’s implementation costs, interactions with other features, and source and ABI compatibility considerations. I’d say that EWG-I tends to skew more towards “user enthusiasm” than EWG does, hence the more permissive outcomes. I’d love for more implementers to spend time in EWG-I, though I do of course realize they’re in short supply and are needed in other subgroups.

Evolution Working Group

As mentioned, I didn’t spend as much time in EWG as usual, but I’ll call out a few of the notable topics that were discussed while I was there.

C++20 NB comments

As with all subgroups, EWG prioritized C++20 NB comments first.

  • The C++20 feature that probably came closest to removal at this meeting was class types as non-type template parameters (NTTPs). Several NB comments pointed out issues with their current specification and asked for either the issues to be resolved, or the feature to be pulled. Thankfully, we were able to salvage the feature. The fix approach involves axing the feature’s relationship with operator==, and instead having template argument equivalence be based on a structural identity, essentially a recursive memberwise comparison. This allows a larger category of types to be NTTPs, including unions, pointers and references to subobjects, and, notably, floating-point types. For class types, only types with public fields are allowed at this time, but future directions for opting in types with private fields are possible.
  • Parenthesized initialization of aggregates also came close to being removed but was fixed instead.
  • A suggestion to patch a functionality gap in std::is_constant_evaluated() by introducing a new syntactic construct if consteval was discussed at length but rejected. The feature may come back in C++23, but there are enough open design questions that it’s too late for C++20.
  • To my mild (but pleasant) surprise, ABI isolation for member functions, a proposal which divorces a method’s linkage from whether it is physically defined inline or out of line, and which was previously discussed as something that’s too late for C++20 but which we could perhaps sneak is as a Defect Report after publication, was now approved for C++20 proper. (It did not get to a plenary vote yet, where it might be controversial.)
  • A minor consistency fix between constexpr and consteval was approved.
  • A few Concepts-related comments:
    • The ability to constrain non-templated functions was removed because their desired semantics were unclear. They could come back in C++23 with clarified semantics.
    • One remaining visual ambiguity in Concepts is that in a template parameter list, Foo Bar can be either a constrained type template parameter (if Foo names a concept) or a non-type template parameter (if Foo names a type). The compiler knows which by looking up Foo, but a reader can’t necessarily tell just by the syntax of the declaration. A comment proposed resolving this by changing the syntax to Foo auto Bar for the type parameter case (similar to the syntax for abbreviated function templates). There was no consensus for this change; a notable counter-argument is that the type parameter case is by far the more common one, and we don’t want to make the common case more verbose (and the non-type syntax can’t be changed because it’s pre-existing).
    • Another comment pointed out that Concept<X> can also mean two different things: a type constraint (which is satisifed by a type T if Concept<T, X> is true), or an expression which evaluates Concept applied to the single argument X. The comment suggested disambiguating by e.g. changing the first case to Concept<, X>, but there was no consensus for this either.

Post-C++20 material

Having gotten through all Evolutionary NB comments, EWG proceeded to review post-C++20 material. Most of this had previously gone through EWG-I (you might recognize a few that I mentioned above because they went through EWG-I this week).

  • (Approved) Reserving attribute names for future use. In addition to approing this for C++23, EWG also approved it as a C++20 Defect Report. (Thanks to Erich Keane for pointing that out!)
  • (Approved) Accessing object representations. EWG agreed with the proposal’s intent and left it to the Core Working Group to figure out the exact way to specify this intent.
  • (Further work) std::fiber_context – stackful context switching. This was discussed at some length, with at least one implementer expressing significant reservations due to the feature’s interaction with thread-local storage (TLS). Several issues related to TLS were raised, such as the fact that compilers can cache pointers to TLS across function calls, and if a function call executes a fiber switch that crosses threads (i.e. the fiber is resumed on a different OS thread), the cache becomes invalidated without the compiler having expected that; addressing this at the compiler lever would be a performance regression even for code that doesn’t use fibers, because the compiler would need to assume that any out of line function call could potentially execute a fiber switch. A possible alternative that was suggested was to have a mechanism for a user-directed kernel context switch that would allow coordinating threads of execution (ToEs) in a co-operative way without needing a distinct kind of ToE (namely, fibers).
  • (Further work) Structured bindings can introduce a pack. EWG liked the direction, but some implementers expressed concerns about the implementation costs, pointing out that in some implementations, handling of packs is closely tied to templates, while this proposal would allow packs to exist outside of templates. The author and affected implementers will discuss the concerns offline.
  • (Further work) Automatically generate more operators. This proposal aims to build on the spaceship operator’s model of rewriting operators (e.g. rewriting a < b to a <=> b < 0), and allow other kinds of rewriting, such as rewriting a += b to a = a + b. EWG felt any such facility should be strictly opt-in (e.g. you could give you class an operator+=(...) = default to opt into this rewriting, but it wouldn’t happen by default), with the exception of rewriting a->b to (*a).b (and the less common a->*b to (*a).*b) which EWG felt could safely happen by default.
  • (Further work) Named character escapes. This would add a syntax for writing unicode characters in source code by using their descriptive names. Most of the discussion concerned the impacts of implementations having to ship a unicode database containing such descriptive names. EWG liked the direction but called for further exploration to minimize such impacts.
  • (Further work) tag_invoke. This concerns making it easier to write robust customization points for library facilities. There was a suggestion of trying to model the desired operations more directly in the language, and EWG suggested exploring that further.
  • (Rejected) Homogeneous variadic function parameters. This would have allowed things like template <typename T> void foo(T...); to mean “foo is a function template that takes zero or more parameters, all of the same type T“. There were two main arguments against this. First, it would introduce a novel interpretation of template-ids (foo<int> no longer names a single specialization of foo, it names a family of specializations, and there’s no way to write a template-id that names any individual specialization). The objection that seems to have played the larger role in the proposal’s rejection, however, is that it breaks the existing meaning of e.g. (int...) as an alternative way of writing (int, ...) (meaning, an int parameter followed by C-style variadic parameters). While the (int...) form is not allowed in C (and therefore, not used by any C libraries that a C++ project might include), apparently a lot of old C++ code uses it. The author went to some lengths to analyze a large dataset of open-source C++ code for occurrences of such use (of which there were vanishingly few), but EWG felt this wasn’t representative of the majority of C++ code out there, most of which is proprietary.

Other Highlights

  • Ville Voutilainen’s paper proposing a high-level direction for C++23 was reviewed favourably by the committee’s Direction Group.
  • While I wasn’t able to attend the Reflection Study Group (SG7)’s meeting (it happened on a day EWG-I was in session), I hear that interesting discussions took place. In particular, a proposal concerning side effects during constant evaluation prompted SG7 to consider whether we should revise the envisioned metaprogramming model and take it even further in the direction of “compile-time programming is like regular programming”, such that if you e.g. wanted compile-time output, then rather than using a facility like the proposed constexpr_report, you could just use printf (or whatever you’d use for runtime output). The Circle programming language was pointed to as prior art in this area. SG7 did not make a decision about this paradigm shift, just encouraged exploration of it.
  • The Concurrency Study Group (SG1) came to a consensus on a design for executors. (Really, this time.)
  • The Networking Study Group (SG4) pondered whether C++ networking facilities should be secure by default. The group felt that we should standardize facilities that make use of TLS if and when they are ready, but not block networking proposals on it.
  • The determistic exceptions proposal was not discussed at this meeting, but one of the reactions that its previous discussions have provoked is a resurgence in interest in better optimizing today’s exceptions. There was an evening session on this topic, and benchmarking and optimization efforts were discussed.
  • web_view was discussed in SG13 (I/O), and I relayed some of Mozilla’s more recent feedback; the proposal continues to enjoy support in this subgroup. The Library Evolution Incubator did not get a chance to look at it this week.

Next Meeting

The next meeting of the Committee will be in Prague, Czech Republic, the week of February 10th, 2020.

Conclusion

This was an eventful and productive meeting, as usual, with the primary accomplishment being improving the quality of C++20 by addressing national body comments. While C++20 is feature-complete and thus no new features were added at this meeting, we did solidify the status of recently added features such as Modules and class types as non-type template parameters, greatly increasing the chances of these features remaining in the draft and shipping as part of C++20.

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 Belfast meeting that you could check out:

A case study in analyzing C++ compiler errors: why is the compiler trying to copy my move-only object?

Recently a coworker came across a C++ compiler error message that seemed baffling, as they sometimes tend to be.

We figured it out together, and in the hope of perhaps saving some others form being stuck on it too long, I thought I’d describe it.

The code pattern that triggers the error can be distilled down into the following:

#include <utility>  // for std::move

// A type that's move-only.
struct MoveOnly {
  MoveOnly() = default;
  
  // copy constructor deleted
  MoveOnly(const MoveOnly&) = delete;  
  
  // move constructor defaulted or defined
  MoveOnly(MoveOnly&&) = default;      
};

// A class with a MoveOnly field.
struct S {
  MoveOnly field;
};

// A function that tries to move objects of type S
// in a few contexts.
void foo() {
  S obj;
  // move it into a lambda
  [obj = std::move(obj)]() {    
    // move it again inside the lambda
    S moved = std::move(obj);   
  }();
}

The error is:

test.cpp: In lambda function:
test.cpp:19:28: error: use of deleted function ‘S::S(const S&)’
   19 |     S moved = std::move(obj);
      |                            ^
test.cpp:11:8: note: ‘S::S(const S&)’ is implicitly deleted because the default definition would be ill-formed:
   11 | struct S {
      |        ^
test.cpp:11:8: error: use of deleted function ‘MoveOnly::MoveOnly(const MoveOnly&)’
test.cpp:6:3: note: declared here
    6 |   MoveOnly(const MoveOnly&) = delete;
      |   ^~~~~~~~

The reason the error is baffling is that we’re trying to move an object, but getting an error about a copy constructor being deleted. The natural reaction is: “Silly compiler. Of course the copy constructor is deleted; that’s by design. Why aren’t you using the move constructor?”

The first thing to remember here is that deleting a function using = delete does not affect overload resolution. Deleted functions are candidates in overload resolution just like non-deleted functions, and if a deleted function is chosen by overload resolution, you get a hard error.

Any time you see an error of the form “use of deleted function F“, it means overload resolution has already determined that F is the best candidate.

In this case, the error suggests S’s copy constructor is a better candidate than S’s move constructor, for the initialization S moved = std::move(obj);. Why might that be?

To reason about the overload resolution process, we need to know the type of the argument, std::move(obj). In turn, to reason about that, we need to know the type of obj.

That’s easy, right? The type of obj is S. It’s right there: S obj;.

Not quite! There are actually two variables named obj here. S obj; declares one in the local scope of foo(), and the capture obj = std::move(obj) declares a second one, which becomes a field of the closure type the compiler generates for the lambda expression. Let’s rename this second variable to make things clearer and avoid the shadowing:

// A function that tries to move objects of type S in a few contexts.
void foo() {
  S obj;
  // move it into a lambda
  [capturedObj = std::move(obj)]() {    
    // move it again inside the lambda
    S moved = std::move(capturedObj);
  }();
}

We can see more clearly now, that in std::move(capturedObj) we are referring to the captured variable, not the original.

So what is the type of capturedObj? Surely, it’s the same as the type of obj, i.e. S?

The type of the closure type’s field is indeed S, but there’s an important subtlety here: by default, the closure type’s call operator is const. The lambda’s body becomes the body of the closure’s call operator, so inside it, since we’re in a const method, the type of capturedObj is const S!

At this point, people usually ask, “If the type of capturedObj is const S, why didn’t I get a different compiler error about trying to std::move() a const object?”

The answer to this is that std::move() is somewhat unfortunately named. It doesn’t actually move the object, it just casts it to a type that will match the move constructor.

Indeed, if we look at the standard library’s implementation of std::move(), it’s something like this:

template <typename T>
typename std::remove_reference<T>::type&& move(T&& t)
{ 
    return static_cast<typename std::remove_reference<T>::type&&>(t); 
}

As you can see, all it does is cast its argument to an rvalue reference type.

So what happens if we call std::move() on a const object? Let’s substitute T = const S into that return type to see what we get: const S&&. It works, we just get an rvalue reference to a const.

Thus, const S&& is the argument type that gets used as the input to choosing a constructor for S, and this is why the copy constructor is chosen (the move constructor is not a match at all, because binding a const S&& argument to an S&& parameter would violate const-correctness).

An interesting question to ask is, why is std::move() written in a way that it compiles when passed a const argument? The answer is that it’s meant to be usable in generic code, where you don’t know the type of the argument, and want it to behave on a best-effort basis: move if it can, otherwise copy. Perhaps there is room in the language for another utility function, std::must_move() or something like that, which only compiles if the argument is actually movable.

Finally, how do we solve our error? The root of our problem is that capturedObj is const because the lambda’s call operator is const. We can get around this by declaring the lambda as mutable:

void foo() {
  S obj;
  [capturedObj = std::move(obj)]() mutable {
    S moved = std::move(capturedObj);
  }();
}

which makes the lambda’s call operator not be const, and all is well.

Trip Report: C++ Standards Meeting in Cologne, July 2019

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 development
Concepts Constrained templates In 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 See below Under active development
Networking TS Sockets library based on Boost.ASIO Published! Not in C++20.
Ranges Range-based algorithms and views In C++20
Coroutines Resumable functions (generators, tasks, etc.) In C++20
Modules A component system to supersede the textual header file inclusion model In C++20
Numerics 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 Pulled from C++20, now targeting C++23
Pattern matching A match-like facility for C++ Under active development, targeting C++23
Reflection TS Static code reflection mechanisms Publication imminent
Reflection v2 A value-based constexpr formulation of the Reflection TS facilities Under active development, targeting C++23
Metaclasses Next-generation reflection facilities Early development

A few links in this blog post may not resolve until the committee’s post-meeting mailing is published (expected within a few days of August 5, 2019). If you encounter such a link, please check back in a few days.

Introduction

Last week I attended a meeting of the ISO C++ Standards Committee (also known as WG21) in Cologne, Germany. This was the second committee meeting in 2019; you can find my reports on preceding meetings here (February 2019, Kona) and here (November 2018, San Diego), and previous ones linked from those. These reports, particularly the Kona one, provide useful context for this post.

This week the committee reached a very important milestone in the C++20 publication schedule: we approved the C++20 Committee Draft (CD), a feature-complete draft of the C++20 standard which includes wording for all of the new features we plan to ship in C++20.

The next step procedurally is to send out the C++20 CD to national standards bodies for a formal ISO ballot, where they have the opportunity to comment on it. The ballot period is a few months, and the results will be in by the next meeting, which will be in November in Belfast, Northern Ireland. We will then spend that meeting and the next one addressing the comments, and then publishing a revised draft standard. Importantly, as this is a feature-complete draft, new features cannot be added in response to comments; only bugfixes to existing features can be made, and in rare cases where a serious problem is discovered, a feature can be removed.

Attendance at this meeting once again broke previous records, with over 200 people present for the first time ever. It was observed that one of the likely reasons for the continued upward trend in attendance is the proliferation of domain-specific study groups such as SG 14 (Games and Low-Latency Programming) and SG 19 (Machine Learning) which is attracting new experts from those fields.

Note that the committe now tracks its proposals 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 Kona report. (As a quick refresher, major features voted in at previous meetings include modules, coroutines, default comparisons (<=>), concepts, and ranges.)

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 focus was on the C++20 CD, and not so much on TSes. In particular, there was no discussion of merging TSes into the C++ IS, because the deadline for doing so for C++20 was the last meeting (where Modules and Coroutines were merged, joining the ranks of Concepts which was merged a few meetings prior), and it’s too early to be discussing mergers into C++23.

Nonetheless, the committee does have a few TSes in progress, and I’ll mention their status:

Reflection TS

The Reflection TS was approved for publication at the last meeting. The publication process for this TS is a little more involved than usual: due to the dependency on the Concepts TS, the Reflection TS needs to be rebased on top of C++14 (the Concepts TS’ base document) for publication. As a result, the official publication has not happened yet, but it’s imminent.

As mentioned before, the facilities in the Reflection TS are not planned to be merged into the IS in their current form. Rather, a formulation based on constexpr values (rather than types) is being worked on. This is a work in progress, but recent developments have been encouraging (see the SG7 (Reflection) section) and I’m hopeful about them making C++23.

Library Fundamentals TS v3

This third iteration (v3) of the Library Fundamentals TS continues to be open for new features. It hasn’t received much attention in recent meetings, as the focus has been on libraries targeted at C++20, but I expect it will continue to pick up material in the coming meetings.

Concurrency TS v2

A concrete plan for Concurrency TS v2 is starting to take shape.

The following features are planned to definitely be included:

The following additional features might tag along if they’re ready in time:

I don’t think there’s a timeline for publication yet; it’s more “when the features in the first list are ready”.

Networking TS

As mentioned before, the Networking TS did not make C++20. As it’s now targeting C++23, we’ll likely see some proposal for design changes between now and its merger into C++23.

One such potential proposal is one that would see the Networking TS support TLS out of the box. JF Bastien from Apple has been trying to get browser implementers on board with such a proposal, which might materialize for the upcoming Belfast meeting.

Evolution Working Group

As usual, I spent most of the week in EWG. 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.

Concepts

  • (Approved) Rename concepts to standard_case for C++20, while we still can. Concepts have been part of the C++ literature long before the C++20 language feature that allows them to be expressed in code; for example, they are discussed in Stepanov’s Elements of Programming, and existing versions of the IS document describe the notional concepts that form the requirements for various algorithms. In this literature, concepts are conventionally named in PascalCase. As a result, the actual language-feature concepts added to the standard library in C++20 were named in PascalCase as well. However, it was observed that essentially every other name in the standard library uses snake_case, and remaining consistent with that might be more important than respecting naming conventions from non-code literature. This was contentious, for various reasons: (1) it was late in the cycle to make this change; (2) a pure mechanical rename resulted in some conflicts with existing names, necessitating additional changes that went beyond case; and (3) some people liked the visual distinction that PascalCase conferred onto concept names. Nonetheless, EWG approved the change.
  • (Approved) On the non-uniform semantics of return-type-requirements. This proposal axes concept requirements of the form expression -> Type, because their semantics are not consistent with trailing return types which share the same syntax.
  • (Approved) Using unconstrained template template parameters with constrained templates. This paper allows unconstrained template template parameters to match constrained templates; without this change, it would have been impossible to write a template template parameter that matches any template regardless of constraints, which is an important use case.

Contracts

Contracts were easily the most contentious and most heavily discussed topic of the week. In the weeks leading up the meeting, there were probably 500+ emails on the committee mailing lists about them.

The crux of the problem is that contracts can have a range of associated behaviours / semantics: whether they are checked, what happens if they are checked and fail, whether the compiler can assume them to be true in various scenarios, etc. The different behaviours lend themselves to different use cases, different programming models, different domains, and different stages of the software lifecycle. Given the diversity of all of the above represented at the committee, people are having a really hard time agreeing on what set of possible behaviours the standard should allow for, what the defaults should be, and what mechanisms should be available to control the behaviour in case you want something other than the defaults.

A prominent source of disagreement is around the possibility for contracts to introduce undefined behaviour (UB) if we allow compilers to assume their truth, particularly in cases where they are not checked, or where control flow is allowed to continue past a contract failure.

Contracts were voted into the C++20 working draft in June 2018; the design that was voted in was referred to as the “staus quo design” during this week’s discussions (since being in the working draft made it the status quo). In a nutshell, in the status quo design, the programmer could annotate contracts as having one of three levels — default, audit, or axiom — and the contract levels were mapped to behaviours using two global switches (controlled via an implementation-defined mechanism, such as a compiler flag): a “build level” and a “continuation mode”.

The status quo design clearly had consensus at the time it was voted in, but since then that consensus had begun to increasingly break down, leading to a slew of Contracts-related proposals submitted for the previous meeting and this one.

I’ll summarize the discussions that took place this week, but as mentioned above, the final outcome was that Contracts was removed from C++20 and is now targeting C++23.

EWG discussed Contracts on two occasions during the week, Monday and Wednesday. On Monday, we started with a scoping discussion, where we went through the list of proposals, and decided which of them we were even willing to discuss. Note that, as per the committee’s schedule for C++20, the deadline for making design changes to a C++20 feature had passed, and EWG was only supposed to consider bugfixes to the existing design, though as always that’s a blurry line.

Anyways, the following proposals were rejected with minimal discussion on the basis of being a design change:

That left the following proposals to be considered. I list them here in the order of discussion. Please note that the “approvals” from this discussion were effectively overturned by the subsequent removal of Contracts from C++20.

  • (Rejected) What to do about contracts? This proposed two alternative minimal changes to the status quo design, with the primary aim of addressing the UB concerns, but neither had consensus. (Another paper was essentially a repeat of one of the alternatives and was not polled separately.)
  • (Rejected) Axioms should be assumable. This had a different aim (allowing the compiler to assume contracts in more cases, not less) and also did not have consensus.
  • (Approved) Minimizing contracts. This was a two-part proposal. The first part removed the three existing contract levels (default, audit, and axiom), as well as the build level and continuation mode, and made the way the behaviour of a contract checking statement is determined completely implementation-defined. The second part essentially layered on top the “Contracts that work” proposal, which introduces literal semantics: rather than annotating contracts with “levels” which are somehow mapped onto behaviours, contracts are annotated with their desired behaviour directly; if the programmer wants different behaviours in different build modes, they can arrange for that themselves, using e.g. macros that expand to different semantics in different build modes. EWG approved both parts, which was somewhat surprising because “Contracts that work” was previously voted as not even being in scope for discussion. I think the sentiment was that, while this is a design change, it has more consensus than the status quo, and so it’s worth trying to sneak it in even though we’re past the design change deadline. Notably, while this proposal did pass, it was far from unanimous, and the dissenting minority was very vocal about their opposition, which ultimately led to the topic being revisited and Contracts being axed from C++20 on Wednesday.
  • (Approved) The “default” contract build-level and continuation-mode should be implementation-defined. This was also approved, which is also somewhat suprising given that it was mooted by the previous proposal. Hey, we’re not always a completely rational bunch!

To sum up what happened on Monday: EWG made a design change to Contracts, and that design change had consensus among the people in the room at the time. Unfortunately, subsequent discussions with people not in the room, including heads of delegations from national standards bodies, made it clear that the design change was very unlikely to have the consensus of the committee at large in plenary session, largely for timing reasons (i.e. it being too late in the schedule to make such a nontrivial design change).

As people were unhappy with the status quo, but there wasn’t consensus for a design change either, that left removing contracts from C++20 and continuing to work on it in the C++23 cycle. A proposal to do so was drafted and discussed in EWG on Wednesday, with a larger group of people in attendance this time, and ultimately garnered consensus.

To help organize further work on Contracts in the C++23 timeframe, a new Study Group, SG 21 (Contracts) was formed, which would incubate and refine an updated proposal before it comes back to EWG. It’s too early to say what the shape of that proposal might be.

I personally like literal semantics, though I agree it probably wouldn’t have been prudent to make a significant change like that for C++20. I would welcome a future proposal from SG 21 that includes literal semantics.

Modules

A notable procedural development in the area of Modules, is that the Modules Study Group (SG 2) was resurrected at the last meeting, and met during this meeting to look at all Modules-related proposals and make recommendations about them. EWG then looked at the ones SG 2 recommended for approval for C++20:

  • (Approved) Mitigating minor Modules maladies. EWG affirmed SG2’s recommendation to accept the first and third parts (concerning typedef names and default arguments, respectively) for C++20.
  • (Approved) Relaxing redefinition restrictions for re-exportation robustness. This proposal makes “include translation” (the automatic translation of some #include directives into module imports) optional, because it is problematic for some use cases, and solves the problems that motivated mandatory include translation in another way. (Aside: Richard Smith, the esteemed author of this paper and the previous one, clearly has too much time on his hands if he can come up with alliterations like this for paper titles. We should give him some more work to do. Perhaps we could ask him to be the editor of the C++ IS document? Oh, we already did that… Something else then. Finish implementing Concepts in clang perhaps? <wink>)
  • (Approved) Standard library header units for C++20. This allows users to consume C++ standard library headers (but not headers inherited from C like <cmath>) using import rather than #include (without imposing any requirements (yet) that their contents actually be modularized). It also reserves module names whose first component is std, or std followed by a number, for use by the standard library.
  • (Approved) Recognizing header unit imports requires full preprocessing. This tweaks the context sensitivity rules for the import keyword in such a way that tools can quickly scan source files and gather their module dependencies without having to do too much processing (and in particular without having to do a full run of the preprocessor).

There were also some Modules-related proposals that SG2 looked at and decided not to advance for C++20, but instead continue iterating for C++23:

  • (Further work) The inline keyword is not in line with the design of modules. This proposal will be revised before EWG looks at it.
  • (Further work) ABI isolation for member functions. EWG did look at this, towards the end of the week when it ran out of C++20 material. The idea here is that people like to define class methods inline for brevity (to avoid repeating the function header in an out-of-line definition), but the effects this has on linkage are sometimes undesirable. In module interfaces in particular, the recently adopted rule changes concerning internal linkage mean that users can run into hard-to-understand errors as a result of giving methods internal linkage. The proposal therefore aims to dissociate whether a method is defined inline or out of line, from semantic effects on linkage (which could still be achieved by using the inline keyword explicitly). Reactions were somewhat mixed, with some concerns about impacts on compile-time and runtime performance. Some felt that if we do this at all, we should do it in C++20, so our guidance to authors of modular code can be consistent from the get-go; while it seems to be too late to make this change in C++20 itself, the idea of a possible future C++20 Defect Report was raised.

Finally, EWG favourably reviewed at the Tooling Study Group’s plans for a C++ Ecosystem Technical Report. One suggestion made was to give the TR a more narrowly scoped name to reflect its focus on Modules-related tooling (lest people are misled into expecting that it addresses every “C++ Ecosystem” concern).

Coroutines

EWG considered several proposed improvements to coroutines. All of them were rejected for C++20 due to being too big of a change at this late stage.

Coroutines will undoubtedly see improvements in the C++23 timeframe, including possibly having some of the above topics revisited, but of course we’ll now be limited to making changes that are backwards-compatible with the current design.

constexpr

  • (Approved) Enabling constexpr intrinsics by permitting unevaluated inline assembly in constexpr functions. With std::is_constant_evaluated(), you can already give an operation different implementations for runtime and compile-time evaluation. This proposal just allows the runtime implementations of such functions to use inline assembly.
  • (Approved) A tweak to constinit: EWG was asked to clarify the intended rules for non-initializing declarations. The Core Working Group’s recommendation — that a non-initializing declaration of a variable be permitted to contain constinit, and if it does, the initializing declaration must be constinit as well — was accepted.

Comparisons

  • (Approved) Spaceship needs a tune-up. This fixes some relatively minor fallout from recent spaceship-related bugfixes.
  • (Rejected) The spaceship needs to be grounded: pull spaceship from C++20. Concerns about the fact that we keep finding edge cases where we need to tweak spaceship’s behaviour, and that the rules have become rather complicated as a result of successive bug fixes, prompted this proposal to remove spaceship from C++20. EWG disagreed, feeling that the value this feature delivers for common use cases outweighs the downside of having complex rules to deal with uncommon edge cases.

Lightweight Exceptions

In one of the meeting’s more exciting developments, Herb Sutter’s lightweight exceptions proposal (affectionately dubbed “Herbceptions” in casual conversation) was finally discussed in EWG. I view this proposal as being particularly important, because it aims to heal the current fracture of the C++ user community into those who use exceptions and those who do not.

The proposal has four largely independent parts:

  • The first and arguably most interesting part (section 4.1 in the paper) provides a lightweight exception handling mechanism that avoids the overhead that today’s dynamic exceptions have, namely that of dynamic memory allocation and runtime type information (RTTI). The new mechanism is opt-in on a per-function basis, and designed to allow a codebase to transition incrementally from the old style of exceptions to the new one.
  • The next two parts have to do with using exceptions in fewer scenarios:
    • The second part (section 4.2) is about transitioning the standard library to handle logic errors not via exceptions like std::logic_error, but rather via a contract violation.
    • The third part (section 4.3) is about handling allocation failure via termination rather than an exception. Earlier versions of the proposal were more aggressive on this front, and aimed to make functions that today only throw exceptions related to allocation failure noexcept. However, that’s unlikely to fly, as there are good use cases for recovering from allocation failure, so more recent versions leave the choice of behaviour up to the allocator, and aim to make such functions conditionally noexcept.
  • The fourth part (section 4.5), made more realistic by the previous two, aims to make the remaining uses of exceptions more visible by allowing expressions that propagate exceptions to be annotated with the try keyword (there being prior art for this sort of thing in Swift and Rust). Of course, unlike Rust, use of the annotation would have to be optional for backwards compatibility, though one can envision enforcing its use locally in a codebase (or part of a codebase) via static analysis.

As can be expected from such an ambitious proposal, this prompted a lot of discussion in EWG. A brief summary of the outcome for each part:

  1. There was a lot of discussion both about how performant we can make the proposed lightweight exceptions, and about the ergonomics of the two mechanisms coexisting in the same program. (For the latter, a particular point of contention was that functions that opt into the new exceptions require a modified calling convention, which necessitates encoding the exception mode into the function type (for e.g. correct calling via function pointers), which fractures the type system). EWG cautiously encouraged further exploration, with the understanding that further experiments and especially implementation experience are needed to be able to provide more informed directional guidance.
  2. Will be discussed jointly by Evolution and Library Evolution in the future.
  3. EWG was somewhat skeptical about this one. In particular, the feeling in the room was that, while Library Evolution may allow writing allocators that don’t throw and library APIs may be revised to take advantage of this and make some functions conditionally noexcept, there was no consensus to move in the direction of making the default allocator non-throwing.
  4. EWG was not a fan of this one. The feeling was that the annotations would have limited utility unless they’re required, and we can’t realistically ever make them required.

I expect the proposal will return in revised form (and this will likely repeat for several iterations). The road towards achieving consensus on a significant change like this is a long one!

I’ll mention one interesting comment that was made during the proposal’s presentation: it was observed that since we need to revise the calling convention as part of this proposal anyways, perhaps we could take the opportunity to make other improvements to it as well, such as allowing small objects to be passed in registers, the lack of which is a pretty unfortunate performance problem today (certainly one we’ve run into at Mozilla multiple times). That seems intriguing.

Other new features

  • (Approved*) Changes to expansion statements. EWG previously approved a “for ...” construct which could be used to iterate at compile time over tuple-like objects and parameter packs. Prior to this meeting, it was discovered that the parameter pack formulation has an ambiguity problem. We couldn’t find a fix in time, so the support for parameter packs was dropped, leaving only tuple-like objects. However, “for ...” no longer seemed like an appropriate syntax if parameter packs are not supported, so the syntax was changed to “template for“. Unfortunately, while EWG approved “template for“, the Core Working Group ran out of time to review its wording, so (*) the feature didn’t make C++20. It will likely be revisited for C++23, possibly including ways to resolve the parameter pack issue.
  • (Further work) Pattern matching. EWG looked at a revised version of this proposal which features a refined pattern syntax among other improvements. The review was generally favourable, and the proposal, which is targeting C++23, is getting close to the stage where standard wording can be written and implementation experience gathered.

Bug / Consistency Fixes

(Disclaimer: don’t read too much into the categorization here. One person’s bug fix is another’s feature.)

For C++20:

For C++23:

  • (Approved) Size feedback in operator new. This allows operator new to communicate to its caller how many bytes it actually allocated, which can sometimes be larger than the requested amount.
  • (Approved) A type trait to detect scoped enumerations. This adds a type trait to tell apart enum classes from plan enums, which is not necessarily possible to do in pure library code.
  • (Approved in part) Literal suffixes for size_t and ptrdiff_t. The suffixes uz for size_t and z for ssize_t were approved. The suffixes t for ptrdiff_t and ut for a corresponding unsigned type had no consensus.
  • (Further work) Callsite based inlining hints: [[always_inline]] and [[never_inline]]. EWG was generally supportive, but requested the author provide additional motivation, and also clarify if they are orders to the compiler (usable in cases where inlining or not actually has a semantic effect), or just strong optimization hints.
  • (Further work) Defaultable default constructors and destructors for all unions. The motivation here is to allow having unions which are trivial but have nontrivial members. EWG felt this was a valid usecase, but the formulation in the paper erased important safeguards, and requested a different formulation.
  • (Further work) Name lookup should “find the first thing of that name”. EWG liked the proposed simplification, but requested that research be done to quantify the scope of potential breakage, as well as archaeology to better understand the motivation for the current rule (which no one in the room could recall.)

Proposals Not Discussed

As usual, there were papers EWG did not get to discussing at this meeting; see the committee website for a complete list. At the next meeting, after addressing any national body comments on the C++20 CD which are Evolutionary in nature, EWG expects to spend the majority of the meeting reviewing C++23-track proposals.

Evolution Working Group Incubator

Evolution Incubator, which acts as a filter for new proposals incoming to EWG, met for two days, and reviewed numerous proposals, approving the following ones to advance to EWG at the next meeting:

Other Working Groups

Library Groups

Having sat in the Evolution group, I haven’t been able to follow the Library groups in any amount of detail, but I’ll call out some of the library proposals that have gained design approval at this meeting:

Note that the above is all C++23 material; I listed library proposals which made C++20 at this meeting above.

There are also efforts in place to consolidate general design guidance that the Library Evolution group would like to apply to all proposals into a policy paper.

While still at the Incubator stage, I’d like to call attention to web_view, a proposal for embedding a view powered by a web browser engine into a C++ application, for the purpose of allowing C++ applications to leverage the wealth of web technologies for purposes such as graphical output, interaction, and so on. As mentioned in previous reports, I gathered feedback about this proposal from Mozilla engineers, and conveyed this feedback (which was a general discouragement for adding this type of facility to C++) both at previous meetings and this one. However, this was very much a minority view, and as a whole the groups which looked at this proposal (which included SG13 (I/O) and Library Evolution Incubator) largely viewed it favourably, as a promising way of allow C++ applications to do things like graphical output without having to standardize a graphics API ourselves, as previously attempted.

Study Groups

SG 1 (Concurrency)

SG 1 has a busy week, approving numerous proposals that made it into C++20 (listed above), as well as reviewing material targeted for the Concurrency TS v2 (whose outline I gave above).

Another notable topic for SG 1 was Executors, where a consensus design was reviewed and approved. Error handling remains a contentious issue; out of two different proposed mechanics, the first one seems to have the greater consensus.

Progress was also made on memory model issues, aided by the presence of several memory model experts who are not regular attendees. It seems the group may have an approach for resolving the “out of thin air” (OOTA) problem (see relevant papers); according to SG 1 chair Olivier Giroux, this is the most optimistic the group has been about the OOTA problem in ~20 years!

SG 7 (Compile-Time Programming)

The Compile-Time Programming Study Group (SG 7) met for half a day to discuss two main topics.

First on the agenda was introspection. As mentioned in previous reports, the committee put out a Reflection TS containing compile-time introspection facilities, but has since agreed that in the C++ IS, we’d like facilities with comparable expressive power but a different formulation (constexpr value-based metaprogramming rather than template metaprogramming). Up until recently, the nature of the new formulation was in dispute, with some favouring a monotype approach and others a richer type hierarchy. I’m pleased to report that at this meeting, a compromise approach was presented and favourably reviewed. With this newfound consensus, SG 7 is optimistic about being able to get these facilities into C++23. The compromise proposal does require a new language feature, parameter constraints, which will be presented to EWG at the next meeting.

(SG 7 also looked at a paper asking to revisit some of the previous design choices made regarding parameter names and access control in reflection. The group reaffirmed its previous decisions in these areas.)

The second main topic was reification, which can be thought of as the “next generation” of compile-time programming facilities, where you can not only introspect code at compile time, but perform processing on its representation and generate (“reify”) new code. A popular proposal in this area is Herb Sutter’s metaclasses, which allow you to “decorate” classes with metaprograms that transform the class definition in interesting ways. Metaclasses is intended to be built on a suite of underlying facilties such as code injection; there is now a concrete proposal for what those facilities could look like, and how metaclasses could be built on top of them. SG 7 looked at an overview of this proposal, although there wasn’t time for an in-depth design review at this stage.

SG 15 (Tooling)

The Tooling Study Group (SG 15) met for a full day, focusing on issues related to tooling around modules, and in particular proposals targeting the C++ Modules Ecosystem Technical Report mentioned above.

I couldn’t be in the room for this session as it ran concurrently with Reflection and then Herbceptions in EWG, but my understanding is that the main outcomes were:

  • The Ecosystem TR should contain guidelines for module naming conventions. There was no consensus to include conventions for other things such as project structure, file names, or namespace names.
  • The Ecosystem TR should recommend that implementations provide a way to implicitly build modules (that is, to be able to build them even in the absence of separate metadata specifying what modules are to be built and how), without requiring a particular project layout or file naming scheme. It was observed that implementing this in a performant way will likely require fast dependency scanning tools to extract module dependencies from source files. Such tools are actively being worked on (see e.g. clang-scan-deps), and the committee has made efforts to make them tractable (see e.g. the tweak to the context-sensitivity rules for import which EWG approved this week).

A proposal for a file format for describing dependencies of source files was also reviewed, and will continue to be iterated on.

One observation that was made during informal discussion was that SG 15’s recent focus on modules-related tooling has meant less time available for other topics such as package management. It remains to be seen if this is a temporary state of affairs, or if we could use two different study groups working in parallel.

Other Study Groups

Other Study Groups that met at this meeting include:

  • SG 2 (Modules), covered in the Modules section above.
  • SG 6 (Numerics) reviewed a dozen or so proposals, related to topics such as fixed-point numbers, type interactions, limits and overflow, rational numbers, and extended floating-point types. There was also a joint session with SG 14 (Games & Low Latency) and SG 19 (Machine Learning) to discuss linear algebra libraries and multi-dimensional data structures.
  • SG 12 (Undefined and Unspecified Behaviour). Topics discussed include pointer provenance, the C++ memory object model, and various other low-level topics. There was also the usual joint session with WG23 – Software Vulnerabilities; there is now a document describing the two groups’ relationship.
  • SG 13 (I/O), which reviewed proposals related to audio (proposal, feedback paper), web_view, 2D graphics (which continues to be iterated on in the hopes of a revised version gaining consensus), as well as few proposals related to callbacks which are relevant to the design of I/O facilities.
  • SG 14 (Games & Low Latency), whose focus at this meeting was on linear algebra proposals considered in joint session with SG 19
  • SG 16 (Unicode). Topics discussed include guidelines for where we want to impose requirements regarding character encodings, and filenames and the complexities they involve. The group also provided consults for relevant parts of other groups’ papers.
  • SG 19 (Machine Learning). In addition to linear algebra, the group considered proposals for adding statistical mathematical functions to C++ (simple stuff like mean, median, and standard deviation — somewhat surprising we don’t have them already!), as well as graph data structures.
  • SG 20 (Education), whose focus was on iterating on a document setting out proposed educational guidelines.

In addition, as mentioned, a new Contracts Study Group (SG 21) was formed at this meeting; I expect it will have its inaugural meeting in Belfast.

Most Study Groups hold regular teleconferences in between meetings, which is a great low-barrier-to-entry way to get involved. Check out their mailing lists here or here for telecon scheduling information.

Next Meeting

The next meeting of the Committee will be in Belfast, Northern Ireland, the week of November 4th, 2019.

Conclusion

My highlights for this meeting included:

  • Keeping the C++ release train schedule on track by approving the C++20 Committee Draft
  • Forming a Contracts Study Group to craft a high-quality, consensus-bearing Contracts design in C++23
  • Approving constexpr dynamic allocation, including constexpr vector and string for C++20
  • The standard library gaining a modern text formatting facility for C++20
  • Broaching the topic of bringing the -fno-exceptions segment of C++ users back into the fold
  • Breaking record attendance levels as we continue to gain representation of different parts of the community on the committee

Due to the 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.

Other Trip Reports

Other trip reports about this meeting include Herb Sutter’s, the collaborative Reddit trip report, Timur Doumler’s and Guy Davidson’s — I encourage you to check them out as well!

Trip Report: C++ Standards Meeting in Kona, February 2019

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.)

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 like int x; at local scope, which leaves x uninitialized. This is currently ill-formed in a constexpr 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 variable const (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

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 by u8 literals changing from type char to char8_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 to std::launder, which facilities writing custom operations that implicitly create objects like malloc(). One point that came up during discussion is that this proposal makes things like implementing a small vector optimization in constexpr 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 of volatile which have little to no practical use, such as volatile-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:

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 enums, to C++, as an alternative to the library-based std::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:

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.

Featured Song: Alone You Breathe

Well, it’s been a while since I wrote one of these 🙂 Not for lack of musical inspiration, mind you — I’ve discovered a number of great bands over the past year that I’ve been itching to share — just been lazy to actually write the posts, I suppose. Going to try and do it more often!

Today’s selection is by Savatage, an American metal band whose members would go on to found the Trans-Siberian Orchestra (whom I’ve featured before).

“Alone You Breathe”, from the 1994 album Handful of Rain is a heartbreakingly sad and beautiful song that Savatage co-founder Jon Oliva wrote about his brother and fellow band member Criss, who had passed away in an accident the previous year.

In a tribute to Criss, the song reuses a stanza of lyrics (quoted below) from “Believe”, another really beautiful Savatage song from their 1991 album Streets: A Rock Opera which Criss wrote.



Featured lyrics:

I am the way, I am the light
I am the dark inside the night
I hear your hopes, I feel your dreams
And in the dark I hear your screams

Full lyrics and some discussion can be found here.

Enjoy!

Trip Report: C++ Standards Meeting in San Diego, November 2018

Summary / TL;DR

<!–

–>

Project What’s in it? Status
C++17 See list Published!
C++20 See below On track
Library Fundamentals TS v3 See below Under active development
Concepts TS Constrained templates Merged into C++20, including (now) 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 Subset headed for C++20, rest in C++23
Concurrency TS v2 See below Under development. Depends on Executors.
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 Published! C++20 merge uncertain
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 On track to be merged into C++20
Numerics TS Various numerical facilities Under active development
Graphics TS 2D drawing API Future uncertain
Reflection TS Static code reflection mechanisms PDTS ballot underway; publication expected in early 2019

A few links in this blog post may not resolve until the committee’s post-meeting mailing is published (expected any day now). If you encounter such a link, please check back in a few days.

Introduction

A few weeks ago I attended a meeting of the ISO C++ Standards Committee (also known as WG21) in San Diego, California. This was the third committee meeting in 2018; you can find my reports on preceding meetings here (June 2018, Rapperswil) and here (March 2018, Jacksonville), and earlier ones linked from those. These reports, particularly the Rapperswil one, provide useful context for this post.

This meeting broke records (by a significant margin) for both attendance (~180 people) and number of proposals submitted (~270). I think several factors contributed to this. First, the meeting was in California, for the first time in the five years that I’ve been attending meetings, thus making it easier to attend for Bay Area techies who weren’t up for farther travels. Second, we are at the phase of the C++20 cycle where the door is closing for new proposals targeting to C++20, so for people wanting to get features into C++20, it was now or never. Finally, there has been a general trend of growing interest in participation in C++ standardization, and thus attendance has been rising even independently of other factors.

This meeting was heavily focused on C++20. As discussed in the committee’s standardization schedule document, this was the last meeting to hear new proposals targeting C++20, and the last meeting for language features with significant library impact to gain design approval. A secondary focus was on in-flight Technical Specifications, such as Library Fundamentals v3.

To accommodate the unprecedented volume of new proposals, there has also been a procedural change at this meeting. Two new subgroups were formed: Evolution Incubator (“EWGI”) and Library Evolution Incubator (“LEWGI”), which would look at new proposals for language and library changes (respectively) before forwarding them to the Evolution or Library Evolution Working Groups (EWG and LEWG). The main purpose of the incubators is to reduce the workload on the main Evolution groups by pre-filtering proposals that need additional work before being productively reviewed by those groups. A secondary benefit was to allow the attendees to be spread out across more groups, as otherwise EWG and LEWG would have likely exceeded their room capacities.

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 Rapperswil report.

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 at the last meeting. 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 PDTS ballot is still ongoing, so there wasn’t much to do on this front at this meeting. We expect the ballot results to be ready by the next meeting (February 2019, in Kona), at which time we’ll address the ballot comments and, time permitting, approve the revised TS for publication.

One minor snafu discovered at this meeting is that prior to the PDTS ballot, the Reflection TS, which depends on Concepts, has been rebased onto C++20, to take advantage of C++20 Concepts (previously, it was based on the Concepts TS). Unfortunately, ISO rules don’t allow publishing a TS before its base document is published, which means that to publish the Reflection TS as-is, we’d have to wait to do it concurrently with the C++20 publication in late 2020. We very much don’t want to wait that long, since the purpose of the Reflection TS is to gather feedback from users in preparation for revised Reflection features in C++23, and the earlier we start getting that feedback, the better. So, we’ll have to un-rebase the Reflection TS onto {C++17 + Concepts TS} to be able to publish it in early 2019 as planned. Isn’t red tape fun?

Library Fundamentals TS v3

This third iteration (v3) of the Library Fundamentals TS is open for new features to be added. (The TS working draft currently contains features from v2 which haven’t been merged into the C++ IS yet.) The only changes voted in at this meeting were a rebase and some issue resolutions, but a number of new features are on the way.

Executors

As discussed below, the revised plans for Executors are for a subset of them to target C++20, and the rest C++23. An Executors TS is not planned at this time.

Merging Technical Specifications into C++20

Turning now to Technical Specifications that have already been published, but not yet merged into the IS, the C++ community is eager to see some of these merge into C++20, thereby officially standardizing the features they contain.

Ranges TS

The Ranges TS modernizes and Conceptifies significant parts of the standard library (the parts related to algorithms and iterators), as well as introducing exciting new features such as range views.

After years of hard work developing these features and going through the TS process, the Ranges TS was finally merged into C++20, paving the way for wider adoption of these features.

Concepts TS

The approval of abbreviated function templates for C++20 at this meeting can be thought of as completing the merge of the Concepts TS into C++20: all the major features in the TS have now been merged, with some design modifications inspired by implementer and user feedback.

While the journey took longer than was initially hoped, in my opinion Concepts is a better feature for the design changes made relative to the Concepts TS, and as such this is an example of the TS process working as intended.

Modules TS

Modules remains one of the most highly anticipated features by the C++ user community. This meeting saw really good progress on Modules: a “merged” Modules design, combining aspects of the Modules TS and the alternative Atom proposal, gained design approval for C++20.

This outcome exceeded expectations in that previously, the merged proposal seemed more likely to target a Modules TS v2 or C++23, with a subset possibly targeting C++20; however, thanks in significant part to the special one-off Modules-focused Bellevue meeting in September, good enough progress was made on the merged design that the authors were comfortable proposing putting the entire thing into C++20, which EWG subsequently approved.

As this is a large proposal, wording review by the Core Working Group will take some time, and as such, a plenary vote to merge the reviewed wording into the C++20 working draft won’t take place until the next meeting or the one after; however, as all the major compiler implementers seem to be on board with this design, and there is overwhelming demand for the feature from the user community, I expect smooth sailing for that vote.

In fewer words: Modules is on track for C++20!

Coroutines TS

The Coroutines TS was once again proposed for merger into C++20 at this meeting. This is the third time this proposal was made (the other two times being at the previous two meetings). At the last meeting, the proposal got as far as a plenary vote at the end of the week, which narrowly failed.

The opposition to merging the TS into C++20 comes from the fact that a number of people have concerns about the Coroutines TS design (some of them are summarized in this paper), and an alternative proposal that addresses these concerns (called “Core Coroutines”) is under active development. Unfortunately, Core Coroutines is not sufficiently-baked to make C++20, so going with it would mean delaying Coroutines until C++23. Opinions differ on whether this is a worthwhile tradeoff: the Core Coroutines authors are of the view that C++ will remain a relevant language for 50 years or more, and waiting 3 years to improve a feature’s design is worthwhile; others have made it clear that they want Coroutines yesterday.

After the failure of last meeting’s merger proposal, it was hoped that waiting one more meeting would allow for the Core Coroutines proposal to mature a bit. While we knew it wouldn’t be ready for C++20, we figured the added maturity would allow us to better understand what we would be giving up by merging the Coroutines TS into C++20, and possibly identify changes we could make the Coroutines TS before C++20’s publication that would make incremental improvements inspired by Core Coroutines backwards-compatible, thereby allowing us to make a more informed decision on the C++20 merger.

Core Coroutines did make significant progress since the last meeting: the updated proposal is simpler, more fleshed out, and has a cleaner syntax. The impasse has also inspired efforts, led by Facebook, to combine the two proposals in such a way that would unblock the merger into C++20, and allow for backwards-comaptible improvements achieving many of the goals of Core Coroutines in C++23, but these efforts are at a relatively early stage (a paper describing the combined design in detail was circulated for the first time while the meeting was underway).

Ultimately, waiting a meeting doesn’t seem to have changed many people’s minds, and we saw a replay of what happened in Rapperswil: EWG narrowly passed the merger, and plenary narrowly rejected it; interestingly, the level of consensus in plenary appears to have decreased slightly since Rapperswil.

To keep C++20 on schedule, the final deadline for approving a TS merger is the next meeting, at Kona. The merger will undoubtedly be re-proposed then, and there remains some optimism that further development of Facebook’s combined proposal might allow us to gain the required confidence in a future evolution path to approve the merger for C++20; otherwise, we’re looking at getting Coroutines in C++23.

Networking TS

It’s looking like the Networking TS will not be merged into C++20, in large part due to the concerns presented this paper discussing usage experience. The TS will instead target C++23.

Evolution Working Group

With the increased number of subgroups meeting in parallel, it’s becoming more challenging to follow what goes on in the committee.

I usually sit in EWG for the duration of the meeting, and summarize the design discussions that take place in that group. I will try to do so again, but I did miss some EWG time while sitting in some study group meetings and Evolution Incubator meetings, so expect some reduction in the amount of detail. If you have specific questions that I didn’t cover, feel free to ask in the comments.

This time, I’ll categorize proposals by topic. For your convenience, I still indicate whether each proposal was approved, had further work on it encouraged, or rejected. Proposals are targeting C++20 unless otherwise mentioned.

Concepts

The headline item here is the approval of the compromise design for abbreviated function templates (AFTs). With this syntax, AFTs look like this:

void f(Concept auto x);

This makes both the “I want to write a function template without the template<...> notation” and the “I want to be able to tell syntactically if a function is a template” camps happy (the latter because the auto tells you the parameter has a deduced type, and therefore the function is a template).

You can also use Concept auto as a return type, and as the type of a variable. In each case, the type is deduced, and the deduced type has to satisy the concept. The paper as written would have allowed the return type and variable cases to omit the auto, but this didn’t have consensus and was removed.

Note that you can write just void f(auto x); as well, making functions consistent with lambdas which could already do this.

Finally, as part of this change, a restriction was imposed on the template <Concept T> notation, that T has to be a type. For non-type and template template parameters, constraints can only be specified using a requires-clause. The motivation here is to be able to tell syntactically what type of entity T is.

A few other Concepts-related proposals were looked at:

  • (Further work) How to make terse notation soar with class template argument deduction. The idea here is to combine class template argument deduction (CTAD) and Concepts such that a class template name (e.g. tuple) can be used as a parameter type as if it were a concept (with the concept being, roughly, “this type is a specialization of tuple“). The proposal was generally well-received, but there are some technical details to iron out, and design alternatives to consider (e.g. spelling it tuple<auto...>), so this will be revisited for C++23.
  • (Rejected) A simple proposal for unifying generic and object-oriented programming. This is a more ambitious proposal to try to allow writing code that works with a set of polymorphic types, that looks the same regardless of whether the polymorphism is dynamic (inheritance) or static (concepts). Reception was mixed; some felt this would introduce a new programming model with relatively little benefit.
  • (Rejected) Concept-defined placeholder types. This would have allowed defining a “placeholder type” constained by a concept, and using that type in place of the concept. It didn’t really fit with the AFT design that was approved.
  • (Rejected) Multi-argument constrained parameter. This proposed a whitespace-based syntax for introducing multiple constrained parameters in a template parameter list, e.g. template <EqualityComparableWith T U>. EWG didn’t feel the whitespace syntax was an improvement over other syntaxes that have been rejected, like template <EqualityComparableWith{T, U}>.

EWG ran out of time to review the updated “constraining Concepts overload sets” proposal. However, there was some informal speculation that the chances of this proposal making C++20 have diminished, because the proposal has grown a lot more complex in an attempt to address EWG’s feedback on the previous version, which suggests that feedback had touched on some hard problems that we may not be in a good position to solve at this time.

Modules

As mentioned, perhaps the biggest high-point of this meeting was EWG’s approval of the merged Modules design for C++20. “Merged” here refers to the proposal combining aspects of the Modules TS design, and the alternative Atom proposal. Perhaps most significantly, the design borrows the Atom proposal’s legacy header imports feature, which is intended to better facilitate incremental transition of existing large codebases to Modules.

Several minor modifications to this design and related changes were also proposed:

  • (Approved) Making module a context-sensitive keyword, take two. Following consistent feedback from many segments of the user community that making module a hard keyword would break too much code, a new proposal for making it context-sensitive, this time with simpler disambiguation rules, was approved.
  • (Approved) Single-file modules with the Atom semantic properties rule. This allows module authors to do certain things that previously required separate module partitions in separate files, in one file.
  • (Approved) Module preamble is unnecessarily fragile. This tweaks the rules for where a module file’s “preamble” (the area containing the module declaration and imports) ends, with a view to making the user model simpler.
  • (Approved) Redefinitions in legacy imports. This clarifies some of the rules in scenarios involving legacy header imports.
  • (Further work) Modules and freestanding. This mostly has to do with how to split the standard library into modules, with the relevance to EWG being that we should have a consistent approach for dealing with freestanding implementations in the language and in the library. EWG did not reach a consensus on this topic, mostly because there are a wide variety of freestanding environments with different constraints, and a single subset of the language does not fit all of them.
  • (Further work) Inline module partitions. This is a generalization of “Single-file modules with the Atom semantic properties rule”, which would allow defining an arbitirary number of module partitions “inline” in a single file. EWG encouraged further development of this idea, but for post-C++20.
  • (Rejected) Global module fragment is unnecessary. The global module fragment is one of two mechanisms for transitioning existing code to Modules (the other being legacy header imports). The author of this paper suggested that just legacy header imports may be sufficient, but this was emphatically argued against based on implementation experience at some companies, leading to the proposal’s rejection.
  • (Rejected) Retiring pernicious language constructs in module contexts. This paper suggested that Modules was an opportunity to shed some of the language’s legacy cruft by making certain constructs invalid inside a module (while they would remain valid in non-modular code for backwards compatibility). There wasn’t much enthusiasm for this idea, largely because it’s expected that people will want to be able to freely copy / migrate code from a non-modular context to a modular context and vice versa.

Contracts

  • (Approved) Access control in contract conditions. This was the subject of a very long and drawn-out debate on the committee mailing lists which I won’t attempt to summarize, but the outcome was that pre- and post-conditions on member functions can reference private and protected variables inside the class, even though we think of them as being part of the class’s public interface.
  • (Approved) Contract postconditions and return type deduction. This is a tweak regarding the interaction between postconditions and return type deduction, with the intention to avoid surprising behaviour. Option 3 from the paper had consensus.
  • (Further work) Allowing contract predicates on non-first declarations. EWG was open to this idea, but some implementation issues (such as who emits the code for the contract check) need to be ironed out.
  • (Further work) Undefined behaviour in contract violations. This was another topic that engendered very extensive mailing list discussion. No decision was made this week, but the likely direction is to specify that contracts (except perhaps axioms) do not allow compilers to assume additional things they couldn’t already assume.
  • (Rejected) Contracts updates. Of the three minor changes proposed in this paper, the first was a trivial wording change (which was approved); the second had no consensus; and the third was deemed unimplementable.

constexpr

Continuing with the committee’s concerted effort to make clunkier forms of compile-time programming (such as template metaprogramming) unnecessary, EWG approved further extensions to constexpr:

Coroutines

I mentioned above that EWG narrowly passed the latest version of a proposal to merge the Coroutines TS into C++20, only to have it rejected in a plenary vote.

The technical discussion of this topic centred around an updated version of the competing Core Coroutines proposal, and a paper by Facebook engineers arguing that most of the benefits of Core Coroutines could be achieved through extensions to the Coroutines TS, and we should therefore go ahead with the Coroutines TS in C++20.

An interesting development that emerged mid-meeting is the Facebook folks coming up with a “unified coroutines” proposal that aims to achieve consensus by combining aspects of the two competing proposals. There wasn’t really enough time for the committee to digest this proposal, but we are all hopeful it will help us make an informed final decision (final for C++20, that is) at the next meeting.

Structured Bindings

  • (Approved in part) Extend structured bindings to be more like variable declarations. Structured bindings can now be static, thread_local, or constexpr; in each case, this applies to the entire composite object being destructured. Rules around linkage were also clarified. Capture of bindings by a lambda was deferred for further work.
  • (Further work) Simplify the customization point for structured bindings. EWG wholehearted wants an overhaul of the customization point (the current one just piggybacks on the customization point for tuple-like that we already had in the language), but felt this proposal addressed just one piece of what is a larger puzzle. A more complete proposal may look something like the operator extract from an earlier pattern matching proposal.
  • (Rejected) Structured bindings with explicit types. This was rejected because the use cases will be addressed more comprehensively with pattern matching.

Class Template Argument Deduction (CTAD)

  • (Approved in part) Filling holes in class template argument deduction. CTAD now works with aggregates, alias templates, and inheriting constructors. Making CTAD work with partial template argument lists was rejected because it would be a breaking change in some cases (e.g. consider vector<any>(MyAlloc())).
  • (Rejected) Improving function templates with CTAD. EWG found that this would involve a lot of complexity, since with function templates you don’t just have one template definition as with class templates, but a whole overload set.

Comparisons

Most comparison-related proposals involved early adopters trying out the spaceship operator (<=>) and discovering problems with it.

  • (Approved) I did not order this! Why is it on my bill?, which probably deserves a medal of some sort for most creative paper title. (Explanation: the paper concerns scenarios where you don’t care about ordering your type, only equality-comparing it, you implement a defaulted operator<=> (because that’s “the C++20 way” for all comparison use cases), and you pay a performance penalty that wouldn’t be there with hand-written code to deal with equality comparison only.) A related paper offers a solution, which is along the lines of making == be its own thing and not fall back to using <=>, since that’s where the inefficiency stems from (for types like string, if the lengths are different you can answer “not equal” much faster than if you’d have to answer “less” or “greater than”). A second part of the proposal, where a defaulted <=> would also generate a defaulted ==, so that users can be largely oblivious to this problem and just default one operator (<=>), was more controversial, but was still approved over some objections.
  • (Approved) When do you actually use <=>? The crux of this paper is that we’ve had to invent a library function compare_3way() wrapping <=> and that’s what we want to use most of the time, so we should just give <=> the semantics of that function.
  • (Mooted) weak_equality considered harmful. This proposal has become moot as implementations of == are no longer generated in terms of <=>. (As a result, weak_equality and strong_equality are no longer used and will likely be removed in the future.)
  • (Rejected) Chaining comparisons. Despite previous encouragement, this was now rejected due to concerns about teachability and implementation issues.

Other New Features

  • (Further work) Deducing this. This proposal allows writing member functions where the type of this is deduced, thereby eliminating the need to duplicate implementations for things like const vs. non-const objects, and other sources of pain. There was a fair amount of technical discussion concerning recursive lambdas (which this proposal hopes to enable), name lookup rules, and other semantic details. The authors will return with a revised proposal.
  • (Rejected) Towards a lazy forwarding mechanism for C++. This would allow declaring function parameters to be “lazy”, such that their arguments are evaluated upon their use inside the function (and possibly not at all if there is no use), rather than at the call site; participants pointed out a similarity to Algol’s “call by name” feature. EWG wasn’t categorically opposed to the notion of lazy parameters, but the notion of having them without any call-site syntax (like this paper proposes) was controversial.

Bug / Consistency Fixes

(Disclaimer: don’t read too much into the categorization here. One person’s bug fix is another’s feature.)

  • (Approved) Allow initializing aggregates from a parenthesized list of values. This finally allows things like vector::emplace_back() to work for aggregates.
  • (Approved) Contra CWG DR1778. This has to do with noexcept and explicitly defaulted functions. The first option from the paper was approved.
  • (Approved) Permit conversions to arrays of unknown bound. The motivation cited for this is working in environments where dynamic allocation is not allowed and use of pointers is restricted, and thus passing around variable-length arrays as arrays of unknown bound are the only way to work with dynamically sized data ranges.
  • (Approved) Array size deduction in new-expressions. This is a minor consistency fix that was also approved as a Defect Report against older language versions.
  • (Approved) Nested inline namespaces. This allows using the C++17 nested namespace syntax in cases where one or more of the namespaces are inline. Example: namespace foo::inline bar::baz { } is short for namespace foo { inline namespace bar { namespace baz { }}}. inline is not allowed in the leading position as people might mistakenly think it applies to the innermost namespace.
  • (Further work) Conditionally trivial special member functions. This is a small but important fix for library implementers who would otherwise have to use labour-intensive techniques to meet the triviality requirements set out for standard library types. This was essentially approved, but specification difficulties necessitate one more round of review.
  • (Further work) Ultimate copy elision. This aims to expand the set of scenarios in which the compiler is allowed to elide copies and moves (note: unlike the C++17 “guaranteed copy elision” feature, this is not requiring compilers to elide copies in these new scenarios, just allowing them). EWG liked the general idea but had concerns about the potential for code breakage in some scenarios.
  • (Further work) Adding the [[constinit]] attribute. The motivation here is cases where you want to guarantee that a variable’s initial value is computed at compile time (so no dynamic initialization required), without making the variable const (so that you can assign new values to it at runtime). EWG liked the idea but preferred using a keyword rather than an attribute. An alternative to decorate the initializer rather than the variable had no consensus.
  • (Postponed) short float. This proposal continues to face challenges due to concerns about different implementations using different sizes for it, or even different representations within the same size (number of bits in mantissa vs. exponent). As a result, there was no consensus for moving forward with it for C++20. There remains strong interest in the topic, so I expect it will come back for C++23, possibly under a different name (such as float16_t instead of short float, to specify the size more concretely).
  • (Rejected) Deprecate the addressof operator. This proposes to deprecate the overloading of operator &. EWG didn’t feel that removal was realistic given that we don’t have a good handle on the breadth of usage in the wild, and didn’t want to entertain deprecation without an intention to remove as a follow-up.

Evolution Working Group Incubator

As mentioned above, due to the increased quantity of proposals, an “EWG Incubator” group (EWGI) was also spun up to do a preliminary round of review on some proposals that EWG couldn’t get to this week, in the hope of making them better-baked for their eventual EWG review at a future meeting.

I only attended EWGI for half a day, so I don’t have much to report about the discussions that went on, but I will list the papers the group forwarded to EWG:

There were also a couple of papers EWGI referred for EWG review not necessarily because they’re sufficiently baked, but because they would benefit from evaluation by a larger group:

Numerous other proposals were asked to return to EWGI with revisions. I’ll call out a couple:

  • There were two proposals for pattern matching. The feature had strong support, and the authors were asked to return with a combined proposal.
  • There was another attempt at named arguments (called “labelled parameters” in the proposal). The novelty in this approach was putting the names in the type system, but without actually modifying any semantic rules like overload resolution, by encoding the labels using existing mechanisms in the type system, and then layering a “sugar” syntax on top. EWGI’s feedback was that the attempted abstraction will leak, and we’ll have to end up making deeper modifications to the type system after all, to have a usable feature. Encouragement to return was weak but existent.

Papers not discussed

There were, of course, also papers that neither EWG nor EWGI had the time to look at during this meeting; among them was Herb’s static exceptions proposal, which is widely anticipated, but not targeting C++20.

I’ll also briefly mention the lifetimebound proposal which Mozillians have expressed a particular interest in due to the increased lifetime safety it would bring: the authors feel that Microsoft’s lifetime checker, whose model of operation is now described in a paper is doing an adequate job of satisfying this use case outside of the core language rules (via annotations + a separate static checker). Microsoft’s lifetime checker ships with MSVC, and has a work-in-progress implementation in Clang as well, which can be tried out in Compiler Explorer, and will hopefully be open-sourced soon. See also Roc’s blog post on this subject.

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:

And a few notable proposals which are still undergoing design review, and are being treated with priority:

There are numerous other proposals in both categories above, I’m just calling out a few that seem particularly noteworthy. Please see the committee’s website for a full list of proposals.

Study Groups

SG 1 (Concurrency)

Most of the C++20-track work (jthread, Executors subset, synchronization omnibus paper, memory model fixes) has progressed out of the Concurrency Study Group and is mentioned above.

For Executors, the current plan is to put a subset of the unified proposal (specifically including “one way” and “bulk one way” executors, but not the other kinds) into C++20, and the rest into C++23; a TS is not planned at this time.

Coroutines-related library additions are not being reviewed yet; they need more bake time, and integration with the next revision of Executors.

SG 1 has opinions on freestanding implementations: they feel omitting thread_local from a freestanding implementation is fine, but omitting non-lock-free atomics or thread-safe statics is more controversial.

SG 7 (Compile-Time Programming)

There were two meetings related to compile-time programming this week. The first was an evening session where the committee re-affirmed its preference for constexpr-based metaprogramming as the future of C++ metaprogramming, in preference to template metaprogramming (TMP). (There was some confusion in this regard, as there was a proposal to standardize Boost.Mp11, a TMP library. The feeling at the end of the meeting was that with constexpr metaprogramming just around the corner, it’s probably not the best use of committee time to standardize a TMP library.)

The second was an afternoon meeting of SG 7, where the main agenda item was reviewing two proposals for reflection based on constexpr metaprogramming: constexpr reflexpr, and scalable reflection in C++. The first is by the authors of the Reflection TS, and tries to carry over the Reflection TS facilities to the land of constexpr in a relatively straightforward way. The second is a variation of this approach that reflects experience gained from experimentation by some implementers. Both proposals also go further than the Reflection TS in functionality, by supporting reification, which involves going from meta-objects obtained via reflection back to the entities they represent.

One notable difference between the two proposals is that the first uses meta-objects of different types to represent different kinds of entities (e.g. meta::variable, meta::function, etc.), whereas the second uses just one type (meta::info) for all meta-objects, and requires using operations on them (e.g. is_variable()) to discriminate. The authors of the second proposal claim this is necessary for compile-time performance to be manageable; however, from an interface point of view the group preferred the different-types approach, and some implementers thought the performance issues could be solved. At the same time, there was agreement that while there should be different types, they should not form an inheritance hierarchy, but rather be type-erased by-value wrappers. In addition, the group felt that errors should be visible in the type system; that is, rather than having meta-objects admit an invalid state, reflection operations that can fail should return something like expected<meta::info> instead.

The target ship vehicle for a constexpr-based reflection facility is not set in stone yet, but people are hopeful for C++23.

In addition, SG 7 approved some guidelines for what kinds of library proposals should require SG 7 review.

SG 13 (Human/Machine Interface)

The Human/Machine Interface Study Group (SG 13) deals with proposals for graphics, event handling, and other forms of user interaction.

Its main product so far has been the 2D graphics proposal, which had been making good progress until it lost consensus to move forward at the last meeting. As there was still significant interest in this proposal in many user communities (see e.g. this paper arguing strongly for it), the Convenor asked SG 13 to have another look at it, to see if consensus could somehow be re-attained. There wasn’t extensive technical discussion of the proposal at this meeting, but we did go over some feedback from potential implementers; it was suggested that the author and other interested parties spend some time talking to graphics experts, many of whom are found in the Bay area (though not the ones at Mozilla – our graphics folks are mostly in the Toronto office).

The group also discussed the web_view proposal, which was positioned as an alternative to rolling our own graphics API. As the proposal effectively involves shipping a web platform implementation as part of the C++ standard library, this proposal has a lot of relevance to Mozilla. As such, I solicited feedback on it on Mozilla’s platform mailing list, and the feedback was pretty universally that this is not a good fit for the C++ standard library. I relayed this feedback at this meeting; nonetheless, the group as a whole was in favour of continuing to pursue this proposal. In fact, the group felt this and 2D graphics serve different use cases and should both be pursued in parallel. (Admittedly, there’s some selection bias going on here: people who choose to attend a meeting of SG 13 are naturally likely to be in favour of proposals in this topic area. I’m curious to see how these proposals will fare in front of a larger voting audience.)

There was also some general discussion of other topics in scope for this group. There are plans for bring forward a proposal for an audio API, and there were also ideas thrown around about things like event handling, user input, sensors, and VR.

SG 15 (Tooling)

The Tooling Study Group (SG 15) met for an evening session, and numerous papers concerning a variety of topics were presented.

The most pressing topic was how to integrate Modules with build systems. The problem is nicely summarized in this paper, and proposed solutions range from a separate “module mapper” component to relying on conventions.

The other major topic was general discussion about where to go in the space of dependency and package management. Ideas presented here include a set of APIs to allow components of a package ecosystem to interface with each other without requiring a particular implementation for any one component, and ideas around package specification.

I don’t feel like a lot of decisions were made in this session, and the group as a whole seems to be conflicted about what its role is given that these areas are not in the purview of the C++ standards document itself, but I still think the evening served as a valuable opportunity for pioneers in these areas to exchange areas and build mindshare around the tooling problems facing the C++ community.

Other Study Groups

Other Study Groups that met at this meeting include:

  • SG 6 (Numerics), which met for about a day and a half and reviewed a dozen or so proposals
  • SG 12 (Undefined and Unspecified Behaviour), which met both on its own (largely due discuss Contracts) and in joint session with WG23 – Software Vulnerabilities (where the focus was on vulnerabilities related to control structures)
  • SG 16 (Unicode), for which this was the first in-person meeting. The group approved a set of high-level priorities in addition to reviewing several specific proposals.

Freestanding Implementations

Not a study group, but this didn’t really fit anywhere else: there was an evening session to try to clarify the committee’s approach to freestanding implementations.

Freestanding implementations are, roughly speaking, those which cannot assume the presence of a full complement of operating system services, because they’re e.g. targeting kernel code or other “bare metal” scenarios; such implementations cannot practically make use of all language features, such as exceptions.

The standard currently defines a subset of the library that is intended to be supported on freestanding implementations, but defines no such subset for the language. Attempts to define such a subset tend to be stymied by the fact that different environments have different constraints, so one subset does not fit all.

The session didn’t reach any firm conclusions, but one possible direction is to avoid trying to define subsets, and instead make it easier for target environments to not use features of the language that are not applicable or practical for it.

New Study Groups

Two new Study Groups were announced at this meeting. Quoting their charters from Herb Sutter’s trip report:

SG 19 (Machine Learning):

We feel we can leverage C++’s strengths in generic programming, optimization and acceleration, as well as code portability, for the specific domain of Machine Learning. The aim of SG19 is to address and improve on C++’s ability to support fast iteration, better support for array, matrix, linear algebra, in memory passing of data for computation, scaling, and graphing, as well as optimization for graph programming.

SG 20 (Education):

We feel we have an opportunity to improve the quality of C++ education, to help software developers correctly use our language and ecosystem to write correct, maintainable, and performing software. SG20 aims to create curriculum guidelines for various levels of expertise and application domains, and to stimulate WG21 paper writers to include advise on how to teach the new feature they are proposing to add to the standard.

Next Meetings

The next meeting of the Committee will be in Kona, Hawaii, the week of February 18th, 2019.

Conclusion

C++ standards development continues to progress at an unprecedented pace. My highlights for this meeting included:

  • Modules gaining design approval to go into C++20
  • Abbreviated function templates reaching consensus, to round out Concepts in C++20
  • Ranges being voted into the C++20 working draft
  • Coroutines continuing to progress towards a unified design that can hopefully achieve consensus

With the big-ticket items above, not to mention Contracts, operator spaceship, and many other goodies, C++20 is shaping up to be a very impressive release!

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.

Other Trip Reports

In addition to Herb’s, other trip reports about this meeting include Corentin Jabot’s, a collaborative Reddit report, and a podcast focused on Library Evolution by Ashley Hedberg. I encourage you to check them out as well!