Trip Report: C++ Standards Meeting in Kona, October 2015

Summary / TL;DR

Project What’s in it? Status
C++14 C++14 Published!
C++17 Very much in flux. Significant language features under consideration include default comparisons, operator., a unified function call syntax, coroutines, and concepts. On track for 2017
Filesystems TS Standard filesystem interface Published!
Library Fundamentals TS I optional, any, string_view and more Published!
Library Fundamentals TS II source code information capture and various utilities Voted out for balloting by national standards bodies
Concepts (“Lite”) TS Constrained templates Publication imminent
Parallelism TS I Parallel versions of STL algorithms Published!
Parallelism TS II TBD. Exploring task blocks, progress guarantees, SIMD. Under active development
Transactional Memory TS Transaction support Published!
Concurrency TS I improvements to future, latches and barriers, atomic smart pointers Voted out for publication!
Concurrency TS II TBD. Exploring executors, synchronic types, atomic views, concurrent data structures Under active development
Networking TS Sockets library based on Boost.ASIO Design review completed; wording review of the spec in progress
Ranges TS Range-based algorithms and views Design review completed; wording review of the spec in progress
Numerics TS Various numerical facilities Beginning to take shape
Array Extensions TS Stack arrays whose size is not known at compile time Direction given at last meeting; waiting for proposals
Reflection Code introspection and (later) reification mechanisms Still in the design stage, no ETA
Graphics 2D drawing API Waiting on proposal author to produce updated standard wording
Modules A component system to supersede the textual header file inclusion model Microsoft and Clang continuing to iterate on their implementations and converge on a design. The feature will target a TS, not C++17.
Coroutines Resumable functions At least two competing designs. One of them may make C++17.
Contracts Preconditions, postconditions, etc. In early design stage

Introduction

Last week I attended a meeting of the ISO C++ Standards Committee in Kona, Hawaii. This was the second committee meeting in 2015; you can find my reports on the past few meetings here (June 2014, Rapperswil), here (November 2014, Urbana-Champaign), and here (May 2015, Lenexa). These reports, particularly the Lenexa one, provide useful context for this post.

The focus of this meeting was primarily C++17. There are many ambitious features underway for standardization, and the time has come to start deciding what which of them will make C++17 and which of them won’t. The ones that won’t will target a future standard, or a Technical Specification (which can eventually also be merged into a future standard). In addition, there are a number of existing Technical Specifications in various stages of completion.

C++17

After C++11, the committee adopted a release train model (much like Firefox’s) for new revisions of the C++ International Standard. Instead of targetting specific features for the next revision of the standard, and waiting until they are all ready before publishing the revision (the way it was done in C++11, which ended up being published 13 years after the previous major revision, C++98), new revisions are released on a three-year train (thus C++14, C++17, C++20, etc.), and a feature is either ready in time to make a particular train, or not (in which case, too bad; it can ride the next train).

As such, there isn’t a list of features planned for C++17 per se. Rather, there are many features in the works, and some will be ready to ride the C++17 train while others will not. More specifically, the committee plans to release the first draft of C++17 for balloting by national standards bodies, called the Committee Draft or CD, at the end of the June 2016 meeting in Oulu, Finland. (This is the timeline required to achieve a publication date in 2017.) This effectively means that for a feature to be in C++17, it must be voted in at that meeting at the latest.

Features already in C++17 coming into the meeting

As a recap, here are the features that have already been voted into C++17 at previous meetings:

I’d classify all of the above as “minor” except for folding expressions, which significantly increase the expressiveness of variadic template code.

Features voted into C++17 at this meeting

Here are the features that have been voted into C++17 at this meeting:

By and large, this is also minor stuff.

Features on the horizon for C++17

Finally, the most interesting bunch: features that haven’t been voted into C++17 yet, but that people are working hard to try and get into C++17.

I’d like to stress that, while obviously the committee tries to standardize features as expeditiously as is reasonable, there is a high quality bar that has to be met for standardization, and a feature isn’t in C++17 until it’s voted in. The features I mention here all have a chance of making C++17 (some better than others), but there are no guarantees.

Concepts

The Concepts Technical Specification (informally called “Concepts Lite”) has been voted for publication in July of this year, and the release of the published TS by ISO is imminent.

The idea behind Technical Specifications is that they give implementers and users a chance to gain experience with a feature, without tying the committee’s hands by standardizing the feature which makes it very difficult to change it in non-backwards-compatible ways. The eventual fate envisioned for a TS is being merged into a future standard, possibly with modifications motivated by feedback from implementers and users.

The question of whether the Concepts TS should be merged into C++17 thus naturally came up. This was certainly the original plan in 2012, when the decision was made to pursue Concepts Lite as a TS aimed for publication in 2014; people then envisioned the TS as being ripe for merger by C++17. Today, with the publication of the TS having slipped to 2015, and the effective deadline for getting changes into C++17 being mid-2016, the timeline looks a bit tighter.

The Concepts TS currently has one complete implementation (modulo bugs), in GCC trunk, which will make it into a release (GCC 6) next year. This, currently experimental, implementation has already been used to gain experience with the feature. Most notably, the Ranges Technical Specification, which revamps significant parts of the C++ Standard Library using Concepts, has an implementation that uses Concepts (in contrast to the previous implementation which emulated Concepts in C++11) and compiles with GCC trunk.

Based on this and other experience, several committee members have argued that Concepts is ripe for standardization in C++17. Others have expressed various concerns about the current design, and argued on this basis that Concepts is not ready for C++17:

  • There is some concern that the current design gives programmers too many syntactic ways of expressing the same thing, and that some of these are redundant. There is no specific proposal for removal one or more of them, but some feel that additional use experience may give insight into what could be removed.
  • Some have argued that Concepts as a language feature, and a revamped standard library that takes advantage of Concepts, should be standardized simultaneously, to demonstrate confidence that the language feature is able to meet the needs of complex, demanding use cases such as those in standard library. The latter is being worked on in the form of the Ranges TS, but that’s quite unlikely to make C++17, so some argue the language feature shouldn’t, either.
  • Some have expressed concern that we don’t yet have a good picture of how well the current Concepts design lends itself to separate checking of template definitions, and that it’s premature to standardize the design until we do.

This last point deserves some elaboration.

When you write a reusable piece of code, such as a function or class, you’re defining an interface between the users of the code, and its implementation. When the function or class is a template, the interface includes requirements on the types (or values) of the template parameters, which are checked at compile time.

Prior to Concepts, these requirements were implicit; you could document the requirements, and you could approximate making them explicit in code by using certain techniques like enable_if, but there was no first-class language support for making them explicit in code. As a result, violations of these requirements – either on the user side by passing template arguments that don’t meet the requirements, or on the implementation side by using the template arguments in ways that go beyond the requirements – would only be caught at instantiation time, that is, when the template is instantiated with concrete arguments for a particular use site. Compiler errors resulting from such violations typically come with long instantiation backtraces, and are notoriously difficult to understand.

Concepts Lite allows us to express these requirements explicitly in code, and to catch violations on the user side “early”, by checking the concrete template arguments passed at a use site against the requirements, without having to look at the implementation of the template. The resulting compiler errors are much easier to understand, and do not contain instantiation backtraces.

However, with the current Concepts Lite design, violations of the requirements on the implementation side – that is, using the template arguments in ways that go beyond the specified requirements – continue to be caught only at instantiation time, and produce hard-to-understand errors with long backtraces. A complete Concepts design, such as the one originally proposed for C++11, includes checking the body of a template against the specified requirements independently of any particular instantiation, to catch implementation-side errors “early” as well; this is referred to as separate checking of template definitions.

Concepts Lite doesn’t currently provide for separate checking of template definitions. Claims have been made that it can be extended to support this, but as this is a difficult-to-implement feature, some would like to see stronger evidence for this (such as a proof-of-concept implementation, or a detailed description of how one would implement it) prior to standardizing Concepts Lite and thus locking us into the current design.

No decision about merging Concepts Lite into C++17 has been made at this meeting; I expect that the topic will continue to be discussed over the next two meetings, with a decision made no later than the June 2016 meeting.

Other published Technical Specifications

The first Parallelism TS, which contains parallel versions of standard algorithms, and was published earlier this year, will be proposed for merger into C++17.

The Filesystem TS, the first Library Fundamentals TS, and the Transactional Memory TS have also been published earlier this year, and could plausibly be proposed for merger into C++17, although I haven’t heard specific talk of such proposals yet.

TS’es that are still in earlier stages of the publication pipeline, such as the Concurrency TS, will almost certainly not make C++17.

Coroutines

As I’ve described in previous posts, there are three competing coroutines proposals in front of the committee:

  • A proposal for stackless coroutines, also known as resumable functions. (This is sometimes called “the await proposal” after one of the keywords it introduces.
  • A proposal for stackful coroutines.
  • A hybrid proposal called resumable expressions that tries to achieve some of the appealing characteristics of both stackful and stackless coroutines.

I talk more about the tradeoffs between stackful and stackless coroutines in my Urbana report, and describe the hybrid approach in more detail in my Lenexa report.

In terms of standardization, at the Urbana meeting the consensus was that stackful and stackless coroutines should advance as independent proposals in the form of a Technical Specification, with a possible view of unifying them in the future. Developments since then can be summed up as follows:

  • The stackless / resumable functions / await proposal has advanced to a stage where it is fully fleshed out, has standard wording, and the Core Working Group has begun reviewing the standard wording.
  • Purely stackful coroutines can be used today as a library-only feature (see e.g. Boost.Coroutine); as such, there is less of a pressing need to standardize them than designs that require language changes.
  • Attempts to achieve a unified proposal are still very much in the design stage. The most recent development on this front is that the author of the “resumable expressions” proposal, Chris Kohlhoff, has decided to abandon the syntax of his proposal, and instead join forces with the authors of the stackful coroutines proposal to come up with an attempt at a unified proposal where the syntax would similar to the one in the stackful proposal, but there would be provisions for the compiler to transform coroutines into a stackless form where possible as an optimization.

Given this state of affairs, and particularly the advanced stage of the await proposal, the question of whether it should be standardized in C++17, rather than a TS, came up. A poll was held on this topic in the Evolution Working Group, with the options being (1) standardizing the await proposal in C++17, and (2) having a TS with both proposals as originally planned. There wasn’t a strong consensus favouring either of these choices over the other; opinion was mostly divided between people who felt we should have some form of coroutines in C++17, and people who felt there was still room for iteration on and convergence between the proposals, making a TS the more appropriate vehicle.

For now, the Core Working Group will continue reviewing the wording of the await proposal; the ship vehicle will presumably be decided by a vote of the full committee at a future meeting.

Contracts

Contracts are a way to express preconditions, postconditions, and other runtime requirements in code, with a view toward opening up use cases such as:

  • Optionally checking the requirements at runtime, and handling their violation in some way.
  • Exposing the requirements to tools such as an analyzer that might attempt to check some of them statically
  • Exposing the requirements to optimizers that might make assumptions (such as assuming that the requirements at met) when optimizing

Initial proposals in the area tended to cater to one or more of these use cases to the detriment of others; guidance from the committee was to aim for a unified proposal. While no such unified proposal has been written yet, the authors of the original proposal and other interested parties have been hard at work trying to solve some of the technical problems involved. I give some details further down.

Given the relatively early stage of these efforts, it’s in my opinion unlikely that they will result in a proposal that makes it into C++17. However, it’s not out of the question, if a written proposal is produced for the next meeting, and its design and wording reviews go sufficiently smoothly.

Operator Dot

Overloading operator dot (the member access operator) allows new forms of interface composition that weren’t possible before.

A proposal for doing so was approved by EWG at the previous meeting; it’s currently pending wording review by CWG.

Another proposal that brings even more expressive power was looked at this meeting; it was sent to the Reflection Study Group as the abilities it unlocks effectively constitute a form of reflection.

The original proposal is slated to come up for a vote to go into C++17 once it passes wording review; however, some object to it on the basis that reflection facilities (such as those that might be produced by iterating on the second proposal) would supersede it. Therefore, I would classify its fate in C++17 as uncertain at this point.

Other Language Features

Beyond the big-ticket items mentioned above, numerous smaller language features may be included in C++17. For a list, please refer to the “Evolution Working Group” section below. Proposals approved by EWG at this meeting are fairly likely to make C++17, as they only need to go through wording review by the Core Working Group before being voted into C++17. Proposals deemed by EWG to need further work face a bit of a higher hurdle, as an updated proposal would need to go through both design and wording reviews before being voted into C++17.

What about Modules?

Modules are possibly the single hottest feature on the horizon for C++ right now. Everyone wants them, and everyone agrees that they will solve very significant problems ranging from code organization to build times. They are also a very challenging feature to specify and implement.

There are two work-in-progress Modules implementations: one in Microsoft’s compiler, and one in Clang, developed primarily by Google. The two implementations take slightly different approaches, and the implementers have been working hard to try to converge their designs.

The Evolution Working Group held a lengthy design discussion about Modules, which culminated in a poll about which of two possible ship vehicles is more appropriate: C++17, or a Technical Specification. A Technical Specification had a stronger consensus, and this is what is currently being pursued. This means Modules are not currently slated for inclusion in C++17. It’s not inconceivable for this to change, if the implementors make very significant progress before the next meeting and convince the committee to change its mind; in my opinion, that’s unlikely to happen.

I talk about the technical issues surrounding Modules in more detail below.

What about <insert favourite feature here>?

Major language features that I haven’t mentioned above, such as reflection, aren’t even being considered for inclusion for C++17.

Evolution Working Group

As usual, I spent practically all of my time in the Evolution Working Group, which spends its time evaluating and reviewing the design of proposed language features.

EWG categorizes incoming proposals into three rough categories:

  • Approved. The proposal is approved without design changes. They are sent on to CWG, which revises them at the wording level, and then puts them in front of the committee at large to be voted into whatever IS or TS they are targeting.
  • Further Work. The proposal’s direction is promising, but it is either not fleshed out well enough, or there are specific concerns with one or more design points. The author is encouraged to come back with a modified proposal that is more fleshed out and/or addresses the stated concerns.
  • Rejected. The proposal is unlikely to be accepted even with design changes.

Accepted proposals:

  • Inline variables allow declaring namespace-scope variables and and static data members as inline, in which case the declaration counts as a definition. (The declaration must then include any initializer.) This is analogous to inline functions, and spares the programmer from having to provide an out-of-line definition in a single translation unit (which can force an otherwise header-only library to no longer be header-only, among other annoyances). The proposal was accepted, with two notable changes. First, to reduce verbosity, constexpr will imply inline. Second, namespace-scope variables will only be allowed to be inline if they are also const. The motivation for the second change is to discourage proliferation of mutable global state; it passed despite objections from some who thought it was complicating the rules with little benefit.
  • A proposal to allow lambdas to appear in a constexpr context. These have good use cases, and there isn’t any compelling motivation to disallow them; the proposal had near unanimous support.
  • Removing dynamic exception specifications, i.e. constructs of the form throw(<list of exception types>) at the end of a function declaration. These have been deprecated since C++11 introduced noexcept. throw() is kept as a (deprecated) synonym for noexcept(true).
  • An extension to aggregate initialization that allows aggregate initialization of types with base classes. The base classes are treated as subobjects, in order of declaration, preceding the class’s own members. Not allowed for classes with virtual bases.
  • constexpr_if, which was called static_if in previous iterations of the proposal. It’s like an if statement, but its condition is evaluated at compile time, and if it appears in a template, the branch not taken is not instantiated. This is neat because it allows code like constexpr_if (/* T is refcounted */) { /* do something */ } constexpr_else { /* do something else */ }; currently, things like this need to be accomplished more verbosely via specialization.
  • Guaranteeing copy elision in certain contexts. Copy elision refers to the compiler eliding (i.e. not performing) a copy or move of an object in some situations. It differs from a pure optimization in that the compiler is allowed to do it even if the elided copy or move constructor has side effects. Every major compiler does this, but it’s not mandatory, and as a result, the language requires the type whose copy or move is elided to still be copyable or movable. This precludes some useful patterns, such as writing a factory function for a non-copyable, non-movable type. This proposal rectifies the problem by requiring that copy elision be performed in certain contexts (specifically, when a temporary object is used to initialize another object; this happens when returning a temporary from a function, initializing a function parameter with a temporary, and throwing a temporary as an exception), and removing the requirement that types which are only notionally copied or moved in those circumstances, be copyable or movable.
  • A proposal to allow an empty enumeration (that is, one with no enumerators) with a fixed underlying type to be constructed from any integer in the range of its underlying type using the EnumName{42} syntax. This was already allowed using the EnumName(42) syntax, but it was considered a narrowing conversion which is not allowed with the {} syntax. This allows using an enumeration as an opaque/strong typedef for an integer type more effectively. It passed despite objections that a full opaque typedefs proposal would make using enums for this purpose unnecessary.
  • A proposal to specify the order of evaluation of operands for all expressions. This is a breaking change, but one people agree we need to make because not sepcifying the order of evaluation leads to a lot of subtle bugs.
  • Unified function call syntax, in one direction only: f(x, y) can resolve to x.f(y) if regular name lookup finds no results for f. This is #3 out of the six design alternatives presented in the original proposal. It satisfies the proposal’s primary use case of making it easier to write generic code using Concepts, by allowing concepts to to require the non-member syntax to work (and have template implementations use the non-member syntax), while a type can be made to model a concept using either member or non-member functions. The other direction (x.f(y) resolving to f(x, y) if member name lookup finds no results for f) was excluded because it was too controversial, as it enabled writing rather brittle code that’s susceptible to “action at a distance”.
  • A proposal to disallow unary folds of some operators over an empty parameter pack was approved by EWG, with the modification that it should be disallowed for all operators. However, the proposal failed to achieve consensus at the plenary meeting, and will not be moving forward at this time.
  • Several modifications to the await proposal (a.k.a. resumable functions / stackless coroutines). Most notably, EWG settled on the keyword choices co_await, co_yield, and co_return; the proposed alternative of “soft keywords” that tried to allow using await and yield without making them keywords, was rejected on the basis that “the difficulty of adding keywords to C++ is a feature”. The various modifications listed in this paper and this one were also accepted.

(I didn’t list features which also passed CWG review and were voted into C++17 at this meeting; those are listed in the the C++17 section above.)

Proposals for which further work is encouraged:

  • Allowing lambdas declared at class scope to capture *this by value. Currently, capturing this captures the containing object by reference; in situations where capture by value is desired (for example, because the lambda can outlive the object), a temporary copy of the object has to be made and then captured, which is easy to forget to do. The paper proposes introducing two new forms in the capture list: *this, to mean “capture the containing object by value”, and *, to mean “capture all referenced variables including the containing object by value”. EWG advised going forward with the first one only (* looks like “capture by pointer”, and we don’t need a third kind of default capture).
  • Dynamic allocation with user-specified alignment. Currently, the user can specify custom alignment for a type using alignas(N), but this does not affect dynamic allocation; the proposal makes it do so. EWG agreed that this should be fixed, but there were some concerns about backward-compatibility; the proposal author will iterate on the proposal to address these concerns.
  • The C++ standard library includes parts of the C standard library by reference. Currently, the referenced version of C is C99. EWG looked at a proposal to update the referenced version to C11. EWG encouraged this, and further suggested that the topic of compatibility between C++ and C in two areas, threads and atomics, be explored.
  • A proposal to obtain the ability the create opaque aliases (a form of typedefs that create a new type) by adding two new language features: function aliases, and allowing inheritance from built-in types. The idea is that the mechanism of creating an opaque alias would be derivation; to allow aliases of built-in types, inheritance from built-in types would be allowed. Function aliases, meanwhile, would provide a mechanism for lifting the operations of the base class into the derived class, so that if you e.g. inherit from int, the inherited operator + could be “aliased” to take and return your derived type instead. EWG liked the idea of function aliases, and encouraged developing it into an independent proposal. Regarding opaque aliases, EWG felt inheritance wasn’t the appropriate mechanism; in particular, deriving from built-in types opens up a can of worms (e.g. “are the operations on int virtual?”). Instead, wrapping the underlying type as a member should be explored as the mechanism for creating opaque aliases. This can already be done today, if you define all the wrapped operations yourself; the language should make that easier to do. It was pointed out that reflection may provide the answer to this.
  • A different approach to overloading “operator dot” where, in the presence of an overloaded operator ., obj.member would resolve to obj.operator.(f), where f is a compiler-generated function object that accepts an argument a of any type, and returns a.member. (Similarly, obj.func(args) would be transformed in the same way, with the function object returning a.func(args)). This proposal has more expressive power than the existing “operator dot” proposal, allowing additional patterns like a form of duck typing (see the paper for a list). EWG liked the abilities this would open up, but wasn’t convinced that “operator dot” was the right spelling for such a feature. In addition, it was pointed out that a lot of these abilities fall under the purview of reflection; EWG recommended continuing to pursue the idea in the Reflection Study Group.
  • A proposal to allow initializer lists to contain movable elements. EWG didn’t find anything objectionable about this per se, but didn’t feel it was sufficiently motivated, and encouraged the author to return with more compelling motivating examples.
  • A proposal to standardize a [[pure]] attribute, which would apply to a function and indicate that the function had no side effects. Everyone wants some form of this, but people disagree on the exact semantics, and how to specify them. The prevailing suggestion seemed to be to specify the behaviour in terms of the “abstract machine” (an abstraction used in the C++ standard text to specify behaviour without getting into implementation-specific details), and to explore standardizing two attributes with related semantics: one to mean “the function can observe the state of the abstract machine, but not modify it”, and another to mean “the function cannot observe the state of the abstract machine, except for its arguments”. To illustrate the difference, a function which reads the value of a global variable (which could change between invocations) would satisfy the first condition, but not the second; such a function has no side effects, but different invocations can potentially return different values. GCC has (non-standard) attributes matching these semantics, [[gnu::pure]] and [[gnu::const]], respectively.
  • A proposal to allow non-type template parameters of deduced type, spelt template <auto V>. This was almost approved, but then someone noticed a potential conflict with the Concepts TS. In the Concepts TS, auto is treated as a concept which is modelled by all types, and in most contexts where auto can be used, so can a concept name. Extending those semantics to this proposal, the meaning of template <ConceptName V> ought to be “a template with a single non-type template parameter whose type is deduced but must satisfy the concept ConceptName“. The problem is, template <ConceptName V> currently has a different meaning in the Concepts TS: “a template with a single type template parameter which must satisfy ConceptName. EWG encouraged the proposal author to work with the editor of the Concepts TS to resolve this conflict, and to propose any resulting feature for addition into the Concepts TS.
  • A revised version of a proposal to allow template argument deduction for constructors. This would allow omitting the template argument list from the name of a class when constructing it, if the template arguments can be deduced from the constructor. The proposal contained two complementary mechanisms for performing the deduction. The first is to perform deduction against the set of constructors of the primary template of the class, as if they were non-member functions and their template parameters included those of the class. The second is to introduce a new construct called a “canonical factory function”, which would be outside the class, and would look something like this:

    template <typename Iter>
    vector(Iter begin, Iter end) -> vector<ValueTypeOf<Iter>>;

    The meaning of this is “if vector is constructed without explicit template arguments and the constructor arguments have type Iter, deduce vector‘s template argument to be ValueTypeOf<Iter>. The proposal author recommended allowing both forms of deduction, and EWG, after discussing both at length, agreed; the author will write standard wording for the next meeting.
  • Extend the for loop syntax to run different code when the loop exited early (using break) than when the loop exited normally. This avoids needing to save enough state outside the loop’s scope to be able to test how the loop exited, which is particularly problematic when looping over ranges with single-pass iterators. EWG agreed this problem is worth solving, but thought the proposal wasn’t nearly well baked enough. A notable concern was that the proposed syntax, if for (...) { /* loop body */} { /* normal exit block */ } else { /* early exit block */ } had the reverse of the semantics of python’s existing for ... else syntax, in which else denotes code to be run if the loop ran to completion.
  • A proposal to allow something like using-declarations inside attribtues. The proposed syntax was [[using(ns), foo, bar, baz]], which would be a shorthand for [[ns::foo, ns::bar, ns::baz]]. EWG liked the idea, but felt some more work was necessary to get the lookup rules right (e.g. what should happen if an attribute name following a using is not found in the namespace named by the using).
  • [[unused]], [[nodiscard]], and [[fallthrough]] attributes, whose meanings are roughly “if this entity isn’t used, that’s intentional”, “it’s important that callers of this function use the return value”, and “this switch case deliberately falls through to the next”. The purpose of the attributes is to allow implementations to give or omit warnings related to these scenarios more accurately; they all exist in the wild as implementation-specific attributes, so standardizing them makes sense. EWG liked these attributes, but slightly preferred the name [[maybe_unused]] for the first, as [[unused]] might wrongly suggest the semantics “this should not be used”. The notion that [[nodiscard]] should be something other than an attribute (such as a keyword) so that the standard can require a diagnostic if the result of a function so marked is discarded, came up but was rejected.
  • A proposal for de-structuring initialization, that would allow writing auto {x, y, z} = expr; where the type of expr was a tuple-like object, whose elements would be bound to the variables x, y, and z (which this construct declares). “Tuple-like objects” include std::tuple, std::pair, std::array, and aggregate structures. The proposal lacked a mechanism to adapt a non-aggregate user-defined type to be “tuple-like” and work with this syntax; EWG’s feedback was that such a mechanism is important. Moreover, EWG recommended that the proposal be expanded to allow (optionally) specifying types for x, y, and z, instead of having their types be deduced.
  • A paper outlining a strategy for unifying the stackless and stackful coroutine proposals. The paper argued that the stackless/stackful distinction is focusing in on one dimension of the design space (the call stack implementation), while there are a number of other dimensions, such as forward progress guarantees, thread-local storage, and lock ownership; it further observed that coroutines have a lot in common with threads, fibres, task-region task, and other similar constructs – collectively, “threads of execution” – and that unification should be sought across all these dimensions. EWG encouraged the author to come back with a fleshed-out proposal.
  • EWG looked at some design issues that came up during wording review of the default comparisons proposal. The most significant one concerned the name lookup rules for auto-generated comparisons. The current wording effectively lexically expands a comparison like a == b into something like a.foo == b.foo && a.bar == b.bar at each call site, performing lookup for each member’s comparison operator at the call site. As these lookups can yield different results for different call sites, the comparison can have different semantics at different call sites. People didn’t like this; several alternatives were proposed along the lines of generating a single, canonical comparison operator for a type, and using it at each call site. An updated proposal that formalizes one of these alternatives is expected at the next meeting.

Rejected proposals:

  • Making void be an object type, so that you can use it the way you can use any object it (that is, you can instantiate it, form a reference to it, and other things currently forbidden for void). The motivation was to ease the writing of generic code, which oftens needs to specialize templates to handle void correctly. EWG agreed the problem was worth solving, but didn’t like this approach; the strongest objections revolved around situations where the inability to use void in a particular way is an important safeguard, such as trying to delete a void*, or trying to perform pointer arithmetic on one, both of which would have to be allowed (or otherwise special-cased) under this scheme.
  • Extension methods, which would allow the first parameter of a non-member function to be named this, and allow calling such functions using the member function call syntax. This is essentially an opt-in version of the “x.f(y) can resolve to f(x, y)” half of the unified function call syntax proposal, which is the half that didn’t pass. It was shot down mostly for the same reasons that the full (no opt-in) version was (concerns about making code more brittle), but even proponents of a full unified call syntax opposed it because they felt an opt-in policy was too restrictive (not allowing existing free functions to be called with a member syntax), and that the safety objectives that motivated opt-in could be better accomplished through Modules.
  • A revised version of the generalized lifetime extension proposal (originally presented in Urbana) that would extend C++’s “lifetime extension” rules. These rules specify that if a local reference variable is bound to a temporary, the lifetime of the temporary (which would normally end at the end of the statement) is extended to match the lifetime of the local variable. The proposal would extend the rules to apply to temporaries appearing as subexpressions of the variable’s initializer, if the compiler can determine that the result of the entire initializer expression refers to one of these temporaries. To make this analysis possible for initializers that contain calls to separately-defined functions, annotations on function parameters (that essentially say whether the result of the function refers to the parameter) would be required. EWG’s view was that the problem this aims to solve (a category of subtle object lifetime issues) is not big enough to warrant requiring such annotations. It’s worth noting that Microsoft is developing a tool that would use annotations of a very similar kind (with well-chosen defaults to reduce verbosity) to warn about such object lifetime issues.
  • noexcept(auto). This wasn’t so much a rejection, as a statement from the proposal author that he does not intend to pursue this proposal further, in spite of EWG having consensus for it at the last meeting. A notable reason for this change in course was the observation that authors of generic code who would benefit most from this feature, often want the function’s return expression to appear in the declaration (such as in the noexcept-specification) for SFINAE purposes.
  • Relaxing a rule about a particular use of unions in constant expressions. If all union members share a “common initial sequence”, then C++ allows accessing something in this sequence through any union member, not just the active one. In constant expressions, this is currently disallowed; the proposal would allow it. Rejected because an implementer argued that it would have a significant negative impact on the performance of their constant expression evaluation.
  • Replacing std::uncaught_exceptions(), previously added to C++17, with a different API that the author argued is harder to misuse. EWG didn’t find this compelling, and there was no consensus to move forward with it.

A proposal for looping simultaneously over multiple ranges was not heard because there was no one available to present it.

Modules

I talked above about the target ship vehicle for Modules being a Technical Specification rather than C++17. Here I’ll summarize the technical discussion that led to this decision.

Modules enable C++ programmers to fundamentally change how they structure their code, and derive many benefits as a result, ranging from improved build times to better tooling. However, programmers often don’t have the luxury of making such fundamental changes to their existing codebases, so there is an enormous demand for implementations to support paradigms that allow reaping some of these benefits with minimal changes to existing code. A big open question is, to what extent should this set of use cases – transitioning existing codebases to a modular world – influence the design of the language feature as standardized, versus being provided for in implementation-specific extensions. Having different answers for this question is the largest source of divergence between the two current Modules implementations.

The most significant issue that this question manifests itself in, is whether modules should “carry” macros; that is, whether macros should be among the set of semantic entities that one module can export and another import.

There are compelling arguments on both sides. On the one hand, due to their nature (being preprocessor constructs, handled during a phase of translation when no syntactic or semantic information is yet available), macros hugely complicate the analysis of C++ code by tools. The vast majority of their uses cases now have non-preprocessor-based alternatives, from const variables and inline functions introduced back in the days of C, to reflection features like source code information capture (to replace things like __FILE__) being standardized today. They are widely viewed as a scourge on the language, and a legacy feature that has no place in new code and does not deserve consideration when designing new language features. As a result, many argue that Modules should not have first-class support for macros. This is the position reflected in Microsoft’s implementation. (It’s important to note that Microsoft’s implementation does have a mechanism for dealing with macros to support transitioning existing codebases (specifically, there exists a compiler flag that can be used when compiling a module that, in addition to generating a module interface file, generates a “side header” containing macro definitions that a consumer of the module can #include in addition to importing the module), but this is strictly an extension and not part of the language feature as they propose it.)

On the other hand, practically all existing large codebases include components that use macros in their interfaces – most notably system headers – and this is unlikely to change in the near future. (As someone put it, “no one is going to rewrite all of POSIX to not use macros any time soon”.) To allow modularizing such codebases in a portable way, many argue that it’s critical that Modules have first-class, standardized support for macros. This is the position reflected in Clang’s implementation.

Factoring into this debate is the state of progress of the two implementations. Microsoft claims to have a substantially complete implementation of their design (where modules do not carry macros), to be released as part of Visual C++ 2015 Update 1, and has submitted a paper with standard wording to the committee. The Clang folks have not yet written such a paper or wording for their design (where modules do carry macros), because they feel their implementation is not yet sufficiently complete that they can be confident that the design works.

EWG discussed all this, and recognized the practical need to support macros in some way, while also recognizing that there is a lot of demand to have some form of Modules available for people to use as soon as possible. A preference was expressed for standardizing Microsoft’s “ready to go” design in some form, while treating the additional abilities conferred by Clang’s design (namely, modules carrying macros) as a possible future extension. The decision to pursue Microsoft’s design as a Technical Specification rather than in C++17 was made primarily because at this stage, we cannot yet be confident that Clang’s design can be expressed as a pure, no-breaking-changes extension to Microsoft’s design. A Technical Specification comes with no guarantee that future standards will be compatible with it, making it the safer and more appropriate choice of ship vehicle.

Later in the week, there was an informal evening session about the implementation of Modules. Many interesting topics were brought up, such as whether the result of compiling a module is suitable as a distribution format for code, and what the implications of that are for things like DRM. Such topics are strictly out of scope of standardization by the committee, but it’s good to hear implementers are thinking about them.

Contracts

Two evening sessions were held on the topic of contract programming, to try to make progress towards standardization. Building on the consensus from the previous meeting that it should be possible to express contracts both in the interface of a function (such as a precondition stated in the declaration) and in the implementation (such as an assertion in the function body), the group tackled some remaining technical challenges.

The security implications of having a global “contract violation handler” which anyone can install, were discussed: it opens up an attack vector where malicious code sets a handler and causes the program to violate a contract, leading to execution of the handler. It was observed that two existing language features, the “terminate handler” (called when std::terminate() is invoked) and the “unexpected handler” (called when an exception is thrown during stack unwinding) have a similar problem, and protection mechanisms employed for those can be applied to the contract violation handler as well.

The thorniest issue was the interaction between a possibly-throwing contract violation handler and a noexcept function: what should happen if a noexcept function has a precondition, the program is compiled in a mode where preconditions are checked, the precondition is checked and fails, the contract violation handler is invoked, and it throws an exception? The possibility of “just allowing it” was considered but rejected, as it would be very problematic for noexcept to mean “does not throw, unless the precondition fails” (code relying on the noexcept would be operating on a possibly-incorrect assumption). The possibilities of not allowing contract violation handlers to throw, and of not allowing preconditions on noexcept functions, were also considered but discarded as being too restrictive. The consensus in the end was, noexcept functions can have preconditions, and contract violation handlers can throw, but if an exception is thrown from a contract violation handler while checking the preconditions of a noecept function, the program will terminate. This is consistent with the more general rule that if an exception is thrown from a noexcept function, the program terminates.

The question of whether a contract violation handler should be allowed to return, i.e. neither throw nor terminate but allow execution of the contract-violated function to proceed, came up, but there was no time for a full discussion on this topic.

Concepts Design Review

When the Concepts TS was balloted by national standards bodies, some of the resulting ballot comments were deferred as design issues to be considered for the next revision of the TS (or its merger into the standard, whichever happens first). EWG looked at these issues at this meeting. Here’s the outcome for the most notable ones:

  • A suggestion to remove terse notation (where void foo(ConceptName c) declares a constrained template function) was rejected on the basis that user feedback about this feature so far has been mostly positive.
  • A suggestion to remove one or more of the four current ways of declaring a constrained template function was rejected on the basis that no specific proposal as to what to remove has been made; the comment authors are welcome to write such a specific proposal if they wish.
  • Unifying the two ways of declaring concepts (as a variable and as a function) was discussed. EWG agreed that this is a worthy goal, but there is no specific proposal on the table. (“Just remove function concepts” isn’t a viable approach because variable concepts cannot be overloaded on the kind and arity of their template parameters, and such overloading is considered an important use case.)
  • Allowing the evaluation of a concept anywhere (such as in a static_assert or a constexpr_if), not just in a requires-clause, was approved.
  • A suggestion to add syntax for same-type constraints (in addition to the existing syntax for “convertible-to” constraints) was rejected on the basis that same-type constraints can easily be expressed as convertible-to constraints with the use of simple helper concept (e.g. { expr } -> Same<T>).

See also the section above where I talk about the bigger picture about Concepts and the possibility of the TS being merged into C++17.

Library / Library Evolution Working Groups

Having spent practically all of my time in EWG, I didn’t have much of a chance to follow developments on the library side of things, but I’ll summarize what I’ve gathered during the plenary sessions.

I already listed library features accepted into C++17 at this meeting (and at previous meetings) above.

Library Fundamentals TS

The first revision of the Library Fundamentals TS was recently published and could be proposed for merger into C++17.

The second revision came into the meeting containing the following:

At this meeting, it picked up the following new features:

The resulting TS has been deemed feature-complete, and will be sent out for balloting by national standards bodies.

array_view, which now has three competing proposals, didn’t make the train for the second revision. It will now likely target the third revision. (As far as I understand, LEWG plans to continue making further revisions to act as a vehicle for standardizing library features that don’t get their own TS.)

Ranges

The Ranges TS, which modernizes significant parts of the standard library by generalizing the notion of iterator pairs to ranges (which are “(iterator, sentinel)” pairs, represented in a single object), while also using the Concepts TS to express requirements using concepts, has passed design review by LEWG, and the LWG has begun reviewing its wording.

A proposed extension, proxy iterators, has been reviewed favourably by LEWG, and is expected to be voted into the TS at the next meeting.

The Ranges TS could be sent out for balloting by national standards bodies as early as the next meeting. Pretty exciting!

The proposal’s author, Eric Niebler, has further extensions in mind, such as range views and infinite ranges. These, however, will likely target a second version of the TS.

Networking

The Networking TS, which was inspired by Boost.ASIO and provides library facilities for socket programming and other asynchronous I/O, continued to undergo wording review. It could be sent out for balloting by national standards bodies as early as the next meeting.

Variant

The design of the proposed std::variant class was a hot topic at this meeting; the pre-meeting mailing contained no fewer than nine papers related to variant. (I won’t link to them all here; if you’re interested, look at the list of papers in the mailing as search for “variant”.)

The most contentious design decision was what to do when a variant<T, U, ...> currently storing a T is assigned a U, and U has a throwing move constructor. Since the storage areas for the T and the U overlap, the T needs to be destroyed before the U is constructed; if this construction then throws, what state is the variant left in? The problem can be solved for the case of a throwing copy constructor: copy the U to a temporary location; if the copy throws, the variant is left in a state where it still stores the T; if the copy succeeds, destroy the T, and move the U from the temporary location into the variant. However, the problem of a throwing move remains.

The proposed alternative solutions include:

  • Allow the variant to store the object on the heap. If U has a throwing move constructor, create it on the heap before destroying the T. If that succeeds, destroy the T, put the variant into heap-storage mode, and point it at the U. This is called double-buffering, and is what boost::variant does, but it had a lot of opposition in the committee from people who wanted a variant that never incurs heap allocation.
  • Disallow types with throwing move constructors. This was argued to be infeasible.
  • Disallow assignment for variants where one of the types has a throwing move constructor.
  • Require one of the elements to be default-constructible, and fall back to default-constructing that if U‘s move constructor throws.
  • Give every variant an empty state, and leave the variant in the empty state if U‘s move constructor throws. This was one of the more popular options, but it had considerable opposition on the basis that it would force people who have no use for such a state logically to do extra checks to consider it.
  • Have an empty state, but only allow it to arise in this exceptional situation, and make it undefined behaviour to access the variant in its emtpy state in any way other than assigning a new value to it. This was the consensus coming out of the previous meeting, but since then people raised objections about the introduction of a new instance of undefined behaviour.

This topic was discussed at length during an evening session; the outcome was a compromise where you have an empty state that can only arise in the exceptional situation, but trying to access it throws an exception rather than incurring undefined behaviour.

A proposal for a language-based variant was also looked at; people liked the idea, but not the syntax, and encouraged continued exploration (without blocking the std::variant proposal) in the context of a more general pattern-matching feature for the language.

std::variant will likely target the third revision of the Library Fundamentals TS.

Study Groups

SG 1 (Concurrency)

The first revision of the Parallelism TS has been published. It will be proposed for inclusion in C++17.

The second revision of the Parallelism TS is under active development. Targeted features include task blocks, wording about progress guarantees, and SIMD support.

The first revision of the Concurrency TS has been voted for publication. It’s less likely to make C++17 than the Parallelism TS.

The second revision of the Concurrency TS is under active development. Targeted feature include executors (for which there are competing proposals), synchronic types, atomic views on non-atomic data, and concurrenct data structures (particularly counters and queues).

There is also work ongoing in the area of thread-locals and GPU support.

SG 5 (Transactional Memory)

The Transactional Memory TS has been published, and has an implementation in GCC (except for the library parts).

The study group has remained active, and is vetting proposals that may target a second revision of the TS.

SG 6 (Numerics)

SG 6 plans to produce a Numerics TS covering the following topics:

SG 7 (Reflection)

SG 7 did not meet this week.

There are currently two proposals for introspection in front of SG 7: the type property queries proposal, and the static reflection proposal. They provide abilities to introspect code at different levels of detail. To move forward, SG 7 would like someone to contribute an analysis of which level of detail is more appropriate for a standardized reflection feature, or whether something in between might be.

The second operator dot proposal, originally presented to EWG, touches on the topic of interface synthesis, and SG 7 intends to take it up.

SG 12 (Undefined Behaviour)

SG 12 looked at updated versions of two proposals that remove undefined behaviour from the preprocessor, and a proposal to define behaviour for some uses of memcpy.

SG 13 (I/O)

No new progress to report; the 2D drawing API proposal, expected to ship as a TS, has passed design review, and SG 13 is continuing to wait for the author to provide standard wording so that wording review can begin.

SG 14 (Game Development & Low-Latency Applications)

SG 14 has been trying to engage with the game development and low-latency application (including financial applications) development communities, to try to foster greater adoption of the standard C++ language and library in these communities. They met at CppCon and held numerous teleconferences between meetings, and met in Kona for an evening session.

The most significant C++ language feature that programmers in these communities tend to opt out of, is exceptions (and by extension, standard library components that can only report errors via exceptions). To try to bridge this gap, a number of possible approaches were brought up:

  • Work with implementers to try to bring the overhead of exception handling down to a minimum
  • Enable exceptions, but consider all functions to be noexcept(true) unless explicitly marked noexcept(false), thus allowing the gradual introduction of exception-enabled code paths
  • Allow marking a namespace or module as noexcept, so that programmers can make their own namespaces or modules noexcept, while still using the standard library

Discussion of this topic continues on the group’s public mailing list; people who are interested are encouraged to participate!

SG 14 is also considering other proposals that cater to the performance needs of these communities, primarily in the area of new standard library functionality:

SG 14 is also following with interest work elsewhere in the committee, such as SG 6’s work on fixed-point real numbers, SG 1’s work on GPU support, new execution policies for the Parallelism TS, and future directions in the area of allocators.

In addition to the next committee meeting in February, SG 14 will meet at GDC next March, to try to engage the game development community more deeply.

Next Meeting

The next meeting of the Committee will be in Jacksonville, Florida, the week of February 29th, 2016.

Conclusion

While the set of major language features that will make C++17 is still very much in flux, it’s a busier and more exciting time for C++ standardization than ever. Within the next few years, the language stands to gain concepts, modules, coroutines, ranges, and contract programming among other things, with reflection features like introspection and interface synthesis around the corner.

I look forward to attending future meetings and reporting on further developments!

17 thoughts on “Trip Report: C++ Standards Meeting in Kona, October 2015

    1. Thanks, good catch. The paper didn’t make the pre-meeting mailing. The link will start working when the post-meeting mailing is published, which should be any day now.

      1. Considering the reaction to Herb Sutter’s poll http://herbsutter.com/2014/12/01/a-quick-poll-about-order-of-evaluation/ the other day I am a little surprised the paper was accepted.

        One of the arguments I have heard was that leaving the order of evaluation unspecified allowed for real optimization opportunities. I just have never heard a good description of how the optimizer could effectively exploit this. Did this argument come up at all? I always wondered how solid that claim was.

      1. > Considering the reaction to Herb Sutter’s poll
        > http://herbsutter.com/2014/12/01/a-quick-poll-about-order-of-evaluation/
        > the other day I am a little surprised the paper was accepted.

        I don’t think the code example given in that post is very representative of the motivation for this proposal. http://wg21.link/n4228 contains more realistic examples (chaining string::replace() and chaining future::then()).

        > One of the arguments I have heard was that leaving the
        > order of evaluation unspecified allowed for real optimization
        > opportunities. I just have never heard a good description of
        > how the optimizer could effectively exploit this. Did this
        > argument come up at all?

        One example that came up during the EWG discussion was that different platform ABIs require pushing function arguments onto the stack in different orders, and it decreases register pressure to evaluate the arguments in the order that they will be pushed.

  1. The “Removing deprecated iostreams aliases” links to “Unstable remove algorithms” Thanks for the great detailed post! I always look forward to your meeting synopses because I can never go myself.

  2. Thank you Botond, for this ridiculously detailed trip report. Herb had a decent overview, and Bjarne’s was nice, but yours is probably the most unbiasedly comprehensive of them all.

    I really hope that concepts lite doesn’t get fully standardized until there is proof that the design will allow for concept checking of the implementation. That has always been my main concern with the design since the early days of concepts lite. And it has always infuriated me that the concepts TS people seem so wiling to ignore or disregard this important aspect of the idea. I sometimes get the feeling like all they care about is concepts lite, that once they get that pushed through the committee, they’ll just shut down the SG and move on to other things.

    I like concepts. But before it becomes an immutable part of C++, it needs to be finished. Or at least, we need to prove that it *can* be finished. I’d hate for us to get trapped into a seemingly-convenient design that we find out later cannot be checked against implementations.

Leave a reply to syaghmour Cancel reply