Trip Report: C++ Standards Meeting in Jacksonville, February 2016

Summary / TL;DR

Project What’s in it? Status
C++17 Filesystem TS, Parallelism TS, Library Fundamentals TS I, if constexpr, and various other enhancements are in. Concepts, coroutines, and unified call syntax are out. Default comparisons and operator. to be decided at next meeting. On track for 2017
Filesystems TS Standard filesystem interface Published! Merging into C++17
Library Fundamentals TS I optional, any, string_view and more Published! Merging into C++17
Library Fundamentals TS II source code information capture and various utilities Resolution of comments from national standards bodies in progress
Concepts (“Lite”) TS Constrained templates Published! NOT merging into C++17
Parallelism TS I Parallel versions of STL algorithms Published! Merging into C++17
Parallelism TS II TBD. Exploring task blocks, progress guarantees, SIMD. Under active development
Transactional Memory TS Transaction support Published! NOT merging into C++17
Concurrency TS I future::then(), latches and barriers, atomic smart pointers Published! NOT merging into C++17
Concurrency TS II TBD. Exploring executors, synchronic types, atomic views, concurrent data structures Under active development
Networking TS Sockets library based on Boost.ASIO Wording review of the spec in progress
Ranges TS Range-based algorithms and views Wording review of the spec in progress
Numerics TS Various numerical facilities Under active development
Array Extensions TS Stack arrays whose size is not known at compile time Withdrawn; any future proposals will target a different vehicle
Modules TS A component system to supersede the textual header file inclusion model Initial TS wording reflects Microsoft’s design; changes proposed by Clang implementers expected. Not targeting C++17..
Graphics TS 2D drawing API Wording review of the spec in progress
Coroutines TS Resumable functions Initial TS wording will reflect Microsoft’s await design; changes proposed by others expected. Not targeting C++17.
Reflection Code introspection and (later) reification mechanisms Design direction for introspection chosen; likely to target a future TS
Contracts Preconditions, postconditions, etc. Unified proposal reviewed favourably. Not targeting C++17.

Introduction

Last week I attended a meeting of the ISO C++ Standards Committee in Jacksonville, Florida. This was the first committee meeting in 2016; you can find my reports on the 2015 meetings here (May 2015, Lenexa) and here (October 2015, Kona). These reports, particularly the Kona one, provide useful context for this post.

This meeting was pointedly focused on C++17. With the window for getting new features into C++17 rapidly closing, it was (and continues to be) crunch time for all the committee subgroups. Many important decisions about what will make C++17 and what won’t were made this week, as I’ll relate below.

Work continued on Technical Specifications (TS’s) as well. As a reminder, TS’s are a mechanism to publish specifications for features that are not quite ready for full standardization; they give implementers and users a chance to try out features while leaving the door open for breaking changes if necessary. Progress was made on both TS’s already in flight (like Networking and Ranges), and new TS’s started for features that didn’t make C++17 (like Coroutines and Modules).

C++17

Recall that, since C++11, the committee has been operating under a release train model, where a new revision of the C++ International Standard (IS) ships every 3 years. For any given revision, whatever features are ready when “the train leaves” are in, and whatever is not ready is out.

For C++17, the train is in the process of leaving. According to the intended schedule, the end of this meeting was the deadline for completing design review for new features, and the end of the next meeting (this June, in Oulu, Finland) will be the deadline for completing wording-level review for the same. This means that, coming out of this meeting, C++17 is essentially feature-complete (although reality is bit more nuanced than that, so read on).

The set of candidate features vying to make C++17 was very large – too large to complete them all and still ship C++17 on time. As a result, hard decisions had to be made about what is ready and what is not. I will describe these decisions and the deliberations that went into them below. My personal opinion is that, while C++17 may not contain everything everyone hoped it might, it will still be an important language revision with a respectable feature set.

So, let’s see what will be in C++17:

Features already in C++17 coming into the meeting

I’ve listed these in my Kona report – see the list of features in C++17 coming into Kona, and the listed of features voted into C++17 at Kona; I won’t repeat them here.

Features voted into C++17 at this meeting

Technical Specifications that have been merged into C++17 at this meeting:

  • The Parallelism TS has been merged into C++17, with the exception of one component, dynamic execution policies, which have been held back and will remain in the second revision of the Parallelism TS for now. There was mild implementer opposition over the lack of implementation experience as part of a portable standard library (as opposed to implementation experience in third-party, mostly non-portable libraries, of which there is plenty), but the vote to merge passed in spite of this. The algorithms defined by this TS will go into namespace std (in the TS they were in std::experimental::parallel), and overload with the existing standard algorithms.
  • Most components of the first Library Fundamentals TS have been merged into C++17. Invocation traits and two small components of polymorphic allocators have been held back, and will remain in the second Library Fundamentals TS for now. The components defined by this TS will go into namespace std (in the TS they were in std::experimental).
  • The Filesystem TS has been merged into C++17 (in its entirety). There was mild opposition to this, mostly by implementers who support systems with non-hierarchical filesystems (such as IBM’s mainframe operating system, z/OS) which do not fit the filesystem model defined by the TS very well, but the vote to merge passed nonetheless. The components defined by this TS will go into namespace std::filesystem (in the TS they were in std::experimental::filesystem).
  • This is actually not a TS but a separate International Standard, but it seemed appropriate to list this it here as well: the Mathematical Special Functions IS has been merged into C++17. This was originally proposed and voted down in Lenexa (two meetings ago), over concerns about implementation difficulty by implementers who did not have userbases particularly clamoring for this functionality; since Lenexa, these worries have been allayed, in part by assurances that the Boost implementation is of sufficiently high quality to serve as a basis for standard library implementations. The functions defined by this IS will go into namespace std (in the IS, since it was targeted at both C and C++, they were in the global namespace).

Other features that have been voted into C++17 at this meeting:

Features that will come up for a vote to go into C++17 in Oulu

As I mentioned above, the last chance to vote new features into C++17 will be at the next meeting, in Oulu. Here I list the features that are expected to come up for such a vote.

It’s important to note that, even after a feature receives design approval from the relevant working group (Evolution for language features, Library Evolution for library features), as all features listed here have, it doesn’t get into C++17 until it’s voted in at a plenary meeting, and sometimes that vote fails (this happened to some features at this meeting). So, while all these features are targeted at C++17, there are no guarantees.

The majority of these features passed design review, or re-review, at this meeting. For the language ones, I discuss them in more detail in the Evolution Working Group section below.

Of the language features listed above, default comparisons and operator dot are slightly controversial; we’ll have to see how they fare at the vote in Oulu.

What was proposed for C++17 but didn’t make it

Unified Call Syntax

Unified call syntax failed to gain consensus, due to concerns that it has the potential to make future code brittle. Specifically, it was pointed out that, since member functions are only considered for a call with non-member syntax if there are no non-member matches, calls with non-member syntax written to rely on this feature to resolve to member functions can break if a matching non-member function is added later, even if the added function is a worse match than the member function. The feature may come up for a vote again at the next meeting if new arguments addressing these concerns are made, but for now it is not going into C++17.

Concepts

The Concepts Technical Specification (also called “Concepts Lite” to reflect that, unlike C++0x concepts, it includes only interface checking, not both interface and definition checking) was published in October 2015.

A proposal to merge the Concepts TS into C++17 was brought forward at this meeting, but it proved fairy controversial and failed to gain consensus. I was going to summarize the reasons why, but then I discovered fellow meeting attendee Tom Honermann’s excellent summary; I can’t possibly put it better or in more detail than he did, so I’ll just refer you to his post.

I’ll share a personal view on this topic: given how important a feature Concepts is for C++, I think’s it’s critical that we get it right, and thus I think the caution displayed in not merging into C++17 is appropriate. I also don’t view Concepts not merging into C++17 as “not having Concepts yet” or “having to wait another N years for Concepts”. We have Concepts, in the form of the Concepts TS. That’s published, and implemented (and is on track to be supported by multiple implementations in the near future), and I don’t see much reason not to use it, including in large production codebases. Yes, it being in a TS means that it can change in backward-incompatible ways, but I don’t expect such changes to be made lightly; to the extent that such changes are made, I would expect them to fall into the following categories:

  • Changes to the concept definition syntax, to be more amenable to separate checking of template definitions. I expect the amount of breakage this causes to be small, because concept definitions are a fairly small amount of code compared to the amount of code that uses these concepts.
  • A possible removal of one or both of the very terse syntaxes for defining constrained functions – the one that omits the template parameter list altogether, and the one that replaces it with a “concept introduction”. I would argue that avoiding these syntaxes in a large codebase is good engineering practice to begin with.
Coroutines

Recapping the status of coroutines coming out of the last meeting:

  • The stackless coroutines proposal championed by Microsoft (a.k.a. the await proposal) already undergoing wording review, but with its ship vehicle (C++17 or a TS) uncertain.
  • Stackful coroutines usable as a library, with a standardization attempt in a relatively early stage.
  • Attempts to create a unified approach (this one and this one) that combines the advantages of stackless (performance) and stackful (no viral effects on a codebase), in an early design stage.

Since then, there have been two interesting developments:

  • A relatively diverse group of people submitted an opinion paper arguing that the await proposal should target a TS, because of a lack of sufficient implementation and deployment experience, and because the design space for alternative approaches (such as the unified approach being sought) is still fairly open.
  • A more fleshed-out proposal for such a unified approach was brought forward. This bore a lot of similarity to the previous “resumable expressions” proposal, but with the significant change that calls across translation unit (TU) boundaries no longer required virtual function calls (which was the strongest criticism of resumable expressions at previous meetings); rather, an annotation system is proposed that allows the compiler to know which functions will be called as part of a coroutine execution, and generate efficient code accordingly.

    To avoid these annotations being viral like await, a mechanism for inferring the annotations within a TU is proposed; this is then extended to handle cross-TU calls by having the compiler generate two versions of a function (one for coroutine execution, and one for normal execution), and having the linker hook up cross-TU calls among these (the Transactional Memory TS uses a similar mechanism to deal with transaction-safe and transaction-unsafe versions of functions).

    The “unified” aspect of this approach comes from the fact that a compiler can use a stackful implementation for part or all of a coroutine execution, without any change to the syntax. While this proposal is obviously still in an early design stage, I got the impression that this was the first time a “unified coroutines” proposal was sufficiently fleshed out to make the committee view it as a serious contender for being the C++ coroutines design.

In light of these developments, a discussion was held on whether the await proposal should target a TS or C++17. The outcome was that a TS had a much stronger consensus, and this was the direction chosen. I would imagine that the unified proposal will then be pursued as a set of changes to this TS, or as a separate proposal in the same TS (or in a different TS).

What wasn’t even proposed for C++17

Modules

In Kona, the committee decided that Modules would target a TS rather than C++17. I said in my Kona report that there’s a chance the committee might change its mind at this meeting, but that I thought it was unlikely. Indeed, the committee did not change its mind; a discussion of this proposal from the authors of the Clang modules implementation to make changes to the design being proposed by Microsoft, made it clear that, while significant convergence has occurred between the two implementations, there still remain gaps to be bridged, and targeting the feature for C++17 would be premature. As a result, Modules will continue to target a TS.

The Modules TS was officially created at this meeting, with its initial working draft consisting of Microsoft’s proposal. (The Clang implementers will formulate updated versions of their proposal as a set of changes to this draft.) The creation of an initial working draft is usually just a procedural detail, but in this case it may be significant, as the GCC and EDG implementers may view it as unblocking their own implementation efforts (as they now have some sort of spec to work with).

I summarize the technical discussion about Modules below.

Concurrency TS

Due to its relatively recent publication (and thus not yet having a lot of time to gain implementation and use experience), the Concurrency TS was not proposed for merging into C++17.

Some people expressed disappointment over this, because the TS contains the very popular future::then() extension.

Transactional Memory TS

For similar reasons, the Transactional Memory TS was not proposed for merging into C++17.

Contracts

Significant design progress has been made on contracts at this meeting. What started out as two very different competing proposals, had by the beginning of the meeting converged (#1, #2) to the point where the remaining differences were largely superficial. During the meeting, these remaining differences were bridged, resulting in a truly unified proposal (paper forthcoming). The proposal is very simple, leaving a lot of features as potential future extensions. However, it’s still too early in the process to realistically target C++17.

Reflection

Reflection was never targeted for C++17, I just mention it because of its popularity. The Reflection Study Group did make significant progress at this meeting, as I describe below.

Evolution Working Group

Having given an overview of the state of C++17, I will now give my usual “fly on the wall” account of the deliberations that went on in the Evolution Working Group (EWG), which reviews the design of proposed language features. I wish I could give a comparably detailed summary of goings-on in the other subgroups but, well, I can only be in one place at a time 🙂

EWG categorizes incoming proposals into three rough categories:

  • Approved. The proposal is approved without design changes. They are sent on to the Core Working Group (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. These proposals are targeting C++17 unless otherwise indicated.
  • 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. At this point, proposals in this category have effectively missed the C++17 train.
  • Rejected. The proposal is unlikely to be accepted even with design changes.

Accepted proposals:

  • Concepts was discussed at length. In addition to debating whether or not the Concepts TS should merge into C++17 (which I’ve touched on above), EWG approved a minor enhancement to the feature: the restriction that requires-expressions can only appear inside a concept definition was relaxed; they can now appear in any expression context.

    The implementation status of Concepts also came up: Clang’s implementation is in progress, and MSVC and EDG both have it on their shortlist of features to implement in the near future; moreover, they have both indicated that Concepts not making it into C++17 will not affect their implementation plans.

  • A revised version of the dynamic memory allocation for over-aligned data proposal was accepted. Some concerns about ABI compatibility were raised, but they were worked out.
  • A revised version of the lambda capture of *this by value proposal was accepted as well. This version only proposes *this as a new kind of thing that can appear in the capture list; that results in the lambda capturing the enclosing object by value, and this inside the lambda referring to the copied object. (The previous version also proposed * to mean a combination of = and *this, but people didn’t like that.) A couple of extension ideas were floated around: allowing moving *this into the lambda, and capturing an arbitrary object to become the this of the lambda; those are left for future exploration.
  • A few changes to the previously accepted proposal to relax initialization rules for enumerations were made. Most notably, the scope of the proposal was expanded to cover all enumerations, not just those with no enumerators, because people felt special-casing the latter would be confusing.
  • Finishing touches were put on the constexpr_if proposal. The proposal has undergone two syntax changes since the last meeting. The original proposed syntax was:
    constexpr_if (...) {
    ...
    } constexpr_else constexpr_if (...) {
    ...
    } constexpr_else {
    ...
    }

    The underscores were then dropped, yielding:
    constexpr if (...) {
    ...
    } constexpr else constexpr if (...) {
    ...
    } constexpr else {
    ...
    }

    Finally, the shorter
    if constexpr (...) {
    ...
    } else if constexpr (...) {
    ...
    } else {
    ...
    }

    was proposed, and this is what was settled on in the end. The proposal was also modified to allow the construct outside of templates; the semantics for such uses is that the branch not taken is fully type-checked, but entities referenced in it are not odr-used, and return statements contained in it do not contribute to return type deduction.
  • Default comparisons passed its final round of design review. The semantic model that EWG ended up settling on is quite simple: an implicitly generated comparison operator for a class behaves as if it had been declared as a friend of the class just before the closing brace of the class declaration. Among other things, this allows libraries to leave unspecified whether their classes use explicit or implicit comparison operators, because users cannot tell the difference. The generation of an implicit operator is triggered the first time the operator is used without an explicit operator being in scope; a subsequent explicit declaration makes the program ill-formed.

    A notable change since the last version of the proposal is that comparing a base object to a derived object is now ill-formed. To keep comparison consistent with copy construction and assignment, slicing during copy construction and assignment will also become ill-formed, unless the derived class doesn’t add any new non-static data members or non-empty bases. This is a breaking change, but the overwhelming majority of cases that would break are likely to be actual bugs, so EWG felt this was acceptable.

    The semantics of <= and >= also came up: should a <= b be implemented as a < b || a == b or as !(a > b)? EWG confirmed its previous consensus that it should be the former, but current standard library facilities such as std::tuple use the latter. EWG felt that the library should change to become consistent with the language, while recognizing that it’s not realistic for this to happen before STL2 (the upcoming non-backwards-compatible overhaul of the STL). There was also some disagreement about the behaviour of wrapper types like std::optional, which wrap exactly one underlying object; some felt strongly that for such type a <= b should be implemented as neither of the above choices, but as a.wrapped_object <= b.wrapped_object.

  • Specifying expression evaluation order was briefly revisited, to choose between two alternatives for the evaluation of operands in a function call expression: left-to-right sequencing, or indeterminate sequencing without any interleaving. Proponents of the second option argued that it was sufficient to fix practically all the real-life bugs that motivated the feature, and that the first option would allow people to design APIs that rely on the left-to-right sequencing of function call expressions, which is seen as undesirable; nonetheless, the first option prevailed.
  • A std::byte type was proposed for the standard library, representing a byte of data without being treated as a character or integer type (the way char flavours are). It’s defined as enum class byte : unsigned char {};. EWG was asked to weigh in on extending C++’s aliasing rules to allow accessing the object representation of any object via a byte*, the way that’s currently allowed via a char*; this extension was approved. An alternative suggestion of allowing this for any scoped enumeration with a character type as its underlying type (as opposing to singling out std::byte) was rejected.
  • Template parameter deduction for constructors passed its final round of design review as well. Following feedback from the previous meeting, the proposal allows deduction via both implicit deduction guides (the constructors of the primary template) and explicit deduction guides (formerly called canonical factory functions). The syntax for the latter is:
    template <typename Iter>
    vector(Iter b, Iter e) -> vector<ValueTypeOf<Iter>>

    placed outside of a class declaration. Injected-class-names retain their meaning (referring to the current instantiation) rather than triggering deduction. A new addition to the proposal is the treatment of partial parameter lists. If a template-id omits some arguments which have defaults, the defaults are used rather than deduction being triggered. However, auto can be used instead of omitting the argument to trigger deduction for that argument only.
    Example:
    std::vector<int> v{myAlloc}; // uses std::allocator<int> (oops)
    std::vector<int, auto> v{myAlloc}; // allocator type deduced from 'myAlloc'
  • Hexadecimal floating-point literals. This is an uncontroversial feature that has been supported by C since C99; it was approved without objection.
  • A shorthand for using multiple attributes in the same attribute namespace, that makes [[using ns: attr1, attr2, attr3]] a shorthand for [[ns::attr1, ns::attr2, ns::attr3]]. In an attribute specifier using this shorthand, all attribute names are looked up in the specified attribute namespace, without fallback to the global attribute namespace. If you want attributes from different namespaces on the same entity, you can either not use the shorthand, or use multiple specifiers as in [[using ns1: a, b, c]] [[using ns2: d, e]].
  • Encouraging compilers to ignore attributes they do not recognize (a variation of part of this proposal). This cannot be made a hard requirement, because an implementation can provide implementation-defined behaviour to an attribute, so an implementation bent on giving an error for attributes it doesn’t know about could claim to recognize all attributes, and provide the behaviour of “giving an error” for all except specific ones.
  • An is_contiguous_layout type trait, which returns true for types for which every bit in the object representation participates in the value representation. Such a trait can be used to implement a higher-level is_uniquely_represented trait, which is true if two objects of the type are equal if and only if their object representations are equal; this is in turn used for hashing. (The difference between the two traits is that is_uniquely_represented can be specialized by the author of a class type, since the author gets to determine whether two objects with the same value representation are considered equal.) is_contiguous_layout requires compiler support to implement, so it was brought in front of EWG, which approved it.
  • template <auto V>, where V is a non-type template parameter whose type is deduced. In Kona it was discovered that this clashes a bit with Concepts, because the natural extension template <ConceptName V> (which, given the relationship between auto and concept names in other contexts, ought to mean a non-type template parameter whose type is deduced but must satisfy ConceptName) already has a meaning in the Concepts TS (namely, a type parameter which must satisfy ConceptName). The proposal author discussed this with with the editor of the Concepts TS, and decided this isn’t an issue: template <ConceptName V> can retain its existing meaning in the Concepts TS, while “a non-type template parameter whose type is constrained by a concept” can be expressed a bit more verbosely as template <auto V> requires ConceptName<decltype(V)>. The feature is targeting C++17, time permitting.

Proposals for which further work is encouraged:

  • An updated version of for loop exit strategies, which uses catch break and catch default to introduce the blocks that run after an early exit and after a normal exit, respectively. EWG liked the direction, but didn’t like the use of the catch keyword, which is strongly associated with exceptions. More generally, there was a reluctance to reuse existing keywords (thus disqualifying the alternative of case break and case default). Other ideas such as context-sensitive keywords (allowing on break and on default, with on being a context-sensitive keyword), and ugly keywords (like on_break and on_default) were proposed, but neither gained consensus. The proposal author said he would think offline about a better spelling.
  • Structured bindings were discussed extensively. The proposed syntax is auto {x, y, z} = expr;, which declares new names x, y, and z, which bind to the “components” of the object expr evaluates to. The notion of “components” is defined for three categories of types:
    1. Array types, for which the components are the array elements.
    2. Class types that support get<N>(o) which returns the Nth component of o, for which the components are whatever these get calls return. Notable examples are std::pair and std::tuple.
    3. Class types where all non-static data members are public, for which the components are the data members. If the type also supports get<>(), that takes precedence.


    Several notable points came up during this discussion:

    • The exact semantics of the feature, with respect to types and object lifetimes, needs to be nailed down. Initially, even the authors of the proposal were confused, but ultimately settled on the following proposed semantics:
      • An unnamed object of the aggregate type is created, as if by auto __o = expr;. Mandatory copy elision ensures that no actual copying is done. Alternatively, the user can use a reference syntax, such as auto& {x, y, z} = expr;, resulting in the unnamed variable correspondingly having reference type, as in auto& __o = expr;.
      • Where possible, x, y, and z are not variables, they are just aliases to components of the unnamed object. The mental model here is to imagine that the compiler re-writes uses of x to be uses of __o.field1 instead. (The alternative would be to make x, y, and z references to the components of the unnamed object, but this wouldn’t support bit-field components, because you can’t have a reference to a bit-field.) I say “where possible”, because this can only be reasonably done for aggregates in categories (1) and (3) above.
      • For aggregates in category (2) above, x, y, and z are variables whose type is deduced as if declared with decltype(auto). (They can’t be aliases in this case, because there’s no knowing what get<>() returns; for example, it could return an object constructed on the fly, so there would be nothing persistent to alias.)
    • The design of the customization point for case (2) needs to be nailed down. There was general agreement that it should be an error to list fewer variables than the aggregate has components; to enforce this for aggregates in category (2), the number of components they have needs to be discoverable, so in addition to get<>() they need to support something like tuple_size<Type>::value. Some people also felt that using re-using get<>() as the customization point is a mistake, and a new function meant specifically for use with this feature should be introduced instead.
    • An alternative syntax of auto [x, y, z] = expr; was proposed by some who argued that it looks more natural, because in a declaration context, having comma-separated things inside braces is unexpected.
    • Some people expressed a strong desire to support explicit types for the components, as in auto { X x, Y y, Z z } = expr;, where X, Y, and Z are type names (or, with the Concepts TS, concept names). This desire sort of clashes with the semantics of having x, y, and z be aliases to the components, because we don’t have the freedom to give them types other than the types of the components. (One can imagine a different semantics, where x, y, and z are copies of the components, where with explicit types we can ask for a components to be converted to some other type; the current semantics do not admit this sort of conversion.) However, it might still make sense to support explicit types and require that they match the types of the components exactly.

    The proposal authors intend to iterate on the proposal, taking the above feedback into account. It’s unlikely to make C++17.

  • A 16-bit floating point type, spelt short float. EWG liked the direction. Some details remain to be worked out, such as the promotion rules (the proposal had short float promote to float, but it was pointed out that because of varags, they need to promote to double instead), and accompanying standard library additions.
  • A class representing a status and an optional value, meant to be used in interfaces where an operation can fail (to handle disappointment, as the author puts it). The class is similar to std::optional, except that it also allows storing a separate status object, whether or not a value object is present. This is useful for environments where exceptions cannot be used, or where immediately throwing an exception is undesirable for some other reason (for example, if we want an exception to be thrown in a delayed fashion, after a return value has made it to another thread or something and we actually try to access it). This was brought in front of EWG, because it’s an attempt to shape the future of error handling in C++. EWG liked the approach, and provided two specific pieces of guidance: first, the class should be marked [[nodiscard]], to indicate that it should not be ignored when used as a return value; and second, that the copyability/movability of the type should reflect that of the underlying value type.
  • A new prefix, 0o, for octal literals. The existing prefix of just 0 makes it too easy to accidentally write an octal literal when you meant a decimal literal. The proposal author would like to eventually deprecate using just 0 as a prefix, though people pointed out this will be difficult due to the heavy use of octal literals in POSIX APIs (for example, for file permission modes).
  • Forward declarations of nested classes. This would allow things like X::A* to appear in a header without requiring a definition for X to also appear in the header (forward-declarations of X and X::A will be sufficient). EWG found the use case compelling, because currently a lot of class definitions to appear in headers only because interfaces defined in the header use pointers or references to nested classes of the type. Several details still need to be worked out. (For example, what happens if a definition of X does not appear in any other translation unit (TU)? What happens if a definition of X appears in another TU, but does not define a nested class A? What happens if it does define a nested class A, but it’s private? The answer to some or all of these may have to be “ill-formed, no diagnostic required”, because diagnosing errors of this sort would require significant linker support.)


Rejected proposals:

  • A proposed change to aggregate initialization rules was rejected. The proposal concerned a scenario where an explicit constructor has one parameter with a default argument. Such a constructor serves as both a converting constructor and a default constructor, and the explicit applies to both, disqualifying the class from certain uses that require a non-explicit default constructor. EWG felt that this is appropriate, and that if the class author intends for only the conversion constructor to be explicit, they can write a separate default constructor instead of using a default argument.
  • Using enumerators of an enum class without qualification in a switch statement was rejected, mostly because people felt that complicating C++’s name lookup rules in this way was not worth the win.
  • Disallowing comma elision in variadic function declarations failed to achieve consensus. The motivation for this proposal was to allow an eventual extension to variadic templates where void foo(int...); would declare a template with a variadic number of int parameters. The problem is, right now void foo(int...) is accepted as an alternative spelling for void foo(int, ...) which is a C-style varags function. This proposal would have disallowed the first spelling, leaving that syntax open for eventually denoting a variadic template. Notably, C already disallows the first spelling, which means this proposal would have improved compatibility with C; it also means any breakage would be limited to C++-only code using C-style varargs functions. Nonetheless, EWG members expressed concerns about code breakage, and the proposal didn’t gain consensus. It’s possible this may be revisited if the author comes back with data about the amount of real-world code breakage (or lack thereof) this would cause.
  • A small change to overload resolution rules, to say that a null pointer constant of integral type (i.e. 0 or NULL) should prefer to convert to std::nullptr_t over void*, failed to achieve consensus. The motivation was to support a proposed library change. Some people didn’t find this compelling enough, and felt we shouldn’t be catering to legacy uses of 0 and NULL as null pointer constants.
  • Not a rejection per se, but EWG decided to withdraw the Array Extensions TS. The design direction didn’t change since change since the Lenexa meeting: rather than dumb “arrays of runtime bound” and a smarter dynarray class to wrap them, we want language syntax for what is really a smarter class type. Withdrawing the TS is just a procedural move that implies that any future proposals will target a different ship vehicle (a new TS, or the IS).
  • Up-to expressions. These would have been expressions of the form [a..b), which would denote a range of values from a up to (but not including) b. The natural use case is integer ranges, although the syntax would have worked with any type that supported operator++. The [a..b) notation was inspired by the mathematical notation for half-open ranges, but implementers were strongly against this use of unbalanced brackets, pointing out that even if they didn’t make the grammar ambiguous strictly speaking, they would break tools’ ability to rely on bracket balancing for heuristics and performance. Alternative syntaxes were brought up, such as a..<b, but overall EWG felt that inventing a syntax for this is unnecessary, when a simple library interface (like range(a, b)) would do. The proposal author was encouraged to work with Eric Niebler, editor of the Ranges TS, to collaborate on such a library facility.
  • Iterating over multiple ranges simultaneously in a range-based for loop. The proposed syntax was for (auto val1 : range1; auto val2 : range2) { ... }. Rejected because structured bindings in combination with a library facility for “zipping” ranges into a single range of tuple-like objects will allow this to be accomplished with syntax like for (auto {val1, val2} : zip(range1, range2)). Concerns were raised about the ability of such a library facility to achieve the same performance as a first-class language feature could, but optimizer developers in the room said that this can be done.
  • Deprecating non-standard attributes in the global attribute namespace (part of this proposal) was rejected, because it would make it more cumbersome to write code that uses an attribute standardized in a particular version of C++ (say C++17), that compiles as both that version of C++, and the previous version (say C++14).
  • Encouraging implementations to diagnose auto operator=(const A&) { ... }, that is, an assignment operator whose return type is deduced using the auto placeholder. Since the auto placeholder always deduced a by-value return type, this is usually a bug (since assigment operators generally return by reference). EWG didn’t feel this was a problem worth solving.

Modules

EWG spent most of an entire day discussing Modules. The developers of Clang’s modules implementation described their experience deploying this implementation across large parts of Google’s codebase, with impressive results (for example, an average 40% reduction of compile times as a result of modularizing the most heavily used 10% of libraries). They then presented the most salient differences between their Modules design and Microsoft’s, formulated as a set of proposed changes to Microsoft’s design.

EWG discussed these proposed changes in depth. The following changes were reviewed favourably:

  • Requiring a module declaration to appear at the top of a file (modulo whitespace and comments). In Microsoft’s proposal, it can be anywhere, and declarations above it are considered to live in the global module. To accomodate declaring things in the global module, the syntax module { /* declarations here live in the global module */ } is proposed. The main motivations are to allow tools to associate files with modules more easily (by not having to parse too far from the beginning of the file), and to avoid imposing a requirement that declarations in the global module must appear before everything else.
  • Allowing the interface of a module to be split across several files. There will still be one primary module interface file, but parts of the module interface can live in secondary files called module partitions. The motivation here is to allow classes with circular dependency between their interfaces to live in two separate files that are partitions of the same module. They are still required to live in the same module; a previous version of Clang’s implementation allowed them to live in different modules, but this necessitated allowing one module to forward-declare entities owned by another module, which placed unfortunate constraints on how modules can be implemented. As a result, they are now required to be in the same module, but requiring them to be in the same file is too restrictive, hence the proposal to split a module interface across files. Importantly, module partitions can be compiled independently. Module partitions bear some resemblance to submodules (of which there is no formal notion in either design), but with the important difference that a module partition cannot be imported by a different module by itself; importing works at the level of entire modules.
  • Using a new syntax, such as module implementation ModuleName;, to begin a module implementation unit (while module interface units use module ModuleName;). The compiler already needs to know whether a translation unit is a module interface unit or a module implementation unit. It currently gets this information from metadata, such as compiler flags or the filename extension. This proposal places that information into the file contents.

The following changes did not yet gain a clear consensus in terms of direction. They will need to be iterated on and discussed further at future meetings:

  • Allowing modules to be found based on their names. This would change the module import syntax from import ModuleName; to import <ModuleName> (or import "ModuleName" or import <path/to/ModuleName>, or any other variation permitted by the #include syntax), with the intention being that it would allow the compiler to find the module based only on its name (together with some per-translation implementation-defined metadata, such as the “#include search path” or an analogue), rather than requiring a separate mapping from module names to their locations. EWG found the motivation to be reasonable, but expressed caution about using strings in this context, and suggested instead using a hierarchical notation that can correspond to a filesystem path rather than being one, such as import x.y.z; standing for the relative path x/y/z (much as with Java imports).
  • Probably the most controversial of the bunch: allowing macros to be exported by a module (and imported by another). Microsoft’s approach is to disallow this, instead requiring authors of a module who wish to export macros to define them in a side header instead, and have client code both import the module and #include the side header. The Clang implementers argued that this was too heavy a burden for module authors and users, particularly in cases where a macro is added to a component’s interface, necessitating a change in how the component is imported. After a lengthy discussion, a basic consensus emerged that there was a lot of demand for both (1) a way to import a module that imported any macros exported by it, and (2) a way to import a module that did not import any macros, and that a Modules proposal would need to support both. The possibility of standardizing these as separate TS’s was brought up, but discarded because it would be confusing for users. No consensus was reached on which of (1) or (2) would be the default (that is, which would simply writing import ModuleName; do), nor on what semantics (2) might have if the module does in fact export macros (the options were to given an error at the import site, and to import the module anyways, silently stripping macros from the set of imported entities).
  • A new mechanism for importing a legacy (non-modularized) header from a module (invoked by some new syntax, like import header "header.h";), which would cause the module to export all macros defined at the end of the header. The motivation for this is that, when transitioning a large codebase to use modules in some order other than strictly bottom-up, a contents of a legacy header can be brought in both through a direct #include, and via a module. Direct inclusion relies, as ever, on an include guard to determine whether the file has been previously included. An inclusion via a module must, therefore, cause that include guard to become defined, to avoid a subsequent direct inclusion bringing in a second copy of the declared entities. The Clang implementers originally tried having this mechanism export just the header guard, but that requires heuristically detecting which macro is the header guard; this can be done in most cases, but is practically impossible for certain headers, such as standard C library headers, which have more complicated inclusion semantics than simple include guards. Another alternative that was tried was allowing multiple inclusion, and performing equivalence merging (a deep structural comparison of two entities) to weed out duplicate definitions, but this was found to be slow (undoing much of the compile-time performance benefit that Modules bring) and error-prone (slight differences in inclusion contexts could lead to two instances of a definition being just different enough not to be merged).

Proposals Not Discussed

EWG has a pretty good track record of getting through most if not all of the proposals on its plate in a given meeting, but at this meeting, due to the amount of time spent discussing Concepts, Coroutines, and Modules, there were 17 papers it did not get a chance to look at. I won’t list them all (see the list of papers on the committee’s website if you’re interested), but I’ll call out three that I was particularly excited about and would have liked to see presented:

  • Default tuple-like access. This would have allowed the mechanisms used to access tuples, get<N>(t), tuple_size<T>::value, and tuple_element<N>::type, to be used on structures whose data members are all public (roughly; the exact conditions are spelled out in the paper). I was particularly excited about this, because it would have unlocked a simple form of reflection (iteration over members) for such structures. However, the proposal probably would have encountered opposition for the same reason (duplicating functionality that will arrive as part of a complete reflection proposal).
  • Overload sets as function arguments. This would have allowed passing the name of a set of overloaded functions as an argument to a function. This is accomplished behind the scenes by wrapping the name in a generic lambda, such as [](auto&&... args) { return f(args...); }. We can do this wrapping today explicitly in C++14 code, but the proposal would have hid it behind a short and intuitive syntax.
  • Return type deduction and SFINAE. This proposal addresses a notable shortcoming of C++14 return type deduction. Return type deduction allows us to write auto foo(args) { return expr; } instead of auto foo(args) -> decltype(expr) { return expr; }. This is great in that avoids repetition, but the second form has a crucial characteristic that the first form lacks: expr is subject to SFINAE in the second form (because it appears in the declaration). The proposal mitigates this shortcoming by extending SFINAE to the function body of a function that uses return type deduction in cases where the invocation happens in an unevaluated context. This approach addresses many use cases without requiring the function body to be mangled into the function name. Extending SFINAE tends to be a hard sell (for example, the C++11 extension of SFINAE to arbitrary expressions had significant opposition), but perhaps this proposal strikes a good balance and will have the potential to achieve consensus.

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 listed library features voted into C++17 at this meeting, and those expected to be voted at the next meeting.

I also mentioned that the first revision of the Library Fundamentals TS and the Filesystem TS have been merged into C++17.

The second revision of the Library Fundamentals TS has been sent out for balloting by national standards bodies at the last meeting. The library groups are now in the process of addressing comments from those ballots. A publication vote is expected in Oulu.

There will be a third revision (and beyond) of the Library Fundamentals TS, but it doesn’t have a formal working draft yet.

std::variant was originally targeted for the third revision of the Library Fundamentals TS, but thanks to its popularity and the degree of confidence in its design, it will now be proposed for C++17!

With the focus being on C++17, LWG did not get a chance to progress the Ranges TS or the Networking TS to a state where they’re ready to be sent out for balloting. Hopefully this will happen in Oulu.

Study Groups

SG 1 (Concurrency)

The first revision of the Parallelism TS has been merged into C++17.

The first revision of the Concurrency TS has been published. It will not merge into C++17.

The second revision of the Parallelism TS is under active development. Targeted features include task blocks, vector/SIMD support (several different proposals), a library for parallel for loops, and execution policies.

The second revision of the Concurrency TS is under active development. Targeted feature include executors (several proposals), synchronic types, atomic views, concurrent queues, distributed counters, and atomic floats.

The planned timeframe for these revisions is feature-complete by the end of 2016, published by the end of 2017.

There was an evening session on the topic of massive parallelism, that featured presentations by HPX and Khronos. Interest was expressed in standardized support for such things, but it was also pointed out that C++ first needs to improve its support for exploiting parallelism in ubiquitous devices (such as phones) before pursuing more specialized form factors.

SG 5 (Transactional Memory)

The Transactional Memory TS was not proposed for merging into C++17.

SG 5 did not meet this week, but plans to meet in the future to discuss proposals that may target a second revision of the TS.

SG 6 (Numerics)

SG 6 met for most of one day, and continued to review candidate proposals for the upcoming Numerics TS. The TS does not have a working draft yet, but an outline of its expected contents, with links to papers proposing components, can be found here.

SG 7 (Reflection)

SG 7 met for two evening sessions, and made very significant progress this week.

Out of the three approaches to static reflection (specifically, introspection) on the table – type property queries, static reflection, and static reflection via template pack expansion – the group arrived at clear guidance to pursue the second (static reflection), and gave the author of that proposal specific guidance on how to progress it.

Several considerations went into the choice of direction. First, it had become clear that we will, if not now then eventually, want to reflect entities that cannot be passed as template parameters, so it made sense to pick a proposal that used an operator syntax (reflexpr(name) in the chosen proposal) rather than a trait syntax. Of the two proposals that used an operator syntax, “static reflection” was preferred because it seemed more flexible, including extensibility to interface synthesis, which the reflection group expects to want to do in the future.

Specific guidance for the author of the “static reflection” proposal included avoiding situations where reflecting over an entity at different points in a program yields different results (due to e.g. additional declarations of the entity having been made, which provide extra information, like an extra default argument), and steering clear of syntax-level reflection (that is, allowing reflection to observe how something was written in code rather than just what the properties of a semantic entity are). The most prominent example of the latter is reflection just telling you the type of a member, versus telling you the specific typedef used to write down that type. While good use cases for the latter have been brought forth, the group views this feature as dangerous territory, and suggests leaving it for a subsequent iteration of the proposal.

SG 7 intends to look at at least one more updated version of the “static reflection” proposal before forwarding it to EWG.

The group also expects to need help from LEWG for library facilities in support of reflection: compile-time strings, which are being worked on, and type lists.

After forwarding an introspection proposal to EWG, SG 7 intends to look at proposals for interface synthesis – the second half of static reflection, where, rather than introspecting entities to get some metadata about them, you take some metadata (that you perhaps have massaged), and reify it into new entities in your program. There is already one such proposal in the queue.

SG 10 (Feature Test)

SG 10 is keeping SD-6, the committee’s standing document listing recommended feature testing macros for various language and library features, up to date. The latest draft be found here.

SG 12 (Undefined Behaviour)

SG 12 hoped to meet this week, but was unable to, due to its chair being tied up in other groups. It hopes to meet instead in Oulu.

SG 13 (Human-Machine Interaction)

The 2D drawing API proposal, expected to ship as a TS, now has standard wording, review of which is in progress.

SG 13 also reviewed a proposal for a standard interface for input event handling; the review was brief because the author was not present in person. A possible interaction with the Networking TS was brought up, as that also has a notion of event queues.

The study group, originally called Graphics and later I/O, has been renamed to Human-Machine Interaction.

SG 14 (Game Development & Low-Latency Applications)

SG 14 has a number of proposals in front of it (see proposals targeted to SG 14 in the list), and they have been holding regular teleconferences, as well as discussions on the group’s public mailing list, about them.

The group did not meet formally this week, but there were two evening sessions on topics it had interest it.

The first was the session on massive parallelism which I described in the SG 1 section above.

The second was a session on a proposal for a packaging system for C++, which I talk about below.

A Packaging System for C++

Addressing questions of code distribution was explicitly made a non-goal of Modules, to limit scope creep, but it was imagined that other proposals that address such questions would follow. This is such a proposal, except that this proposal endeavours to support both Modular and non-Modular code.

The proposal consists of two parts: a specification for a packaging system itself, which is clearly out of scope for the Standard (but may be in scope for something else produced by the committee, such as a Standing Document); and some language syntax (#using PackageName) to interface with the packaging system, which would need to be standardized.

Three main pieces of feedback were given in the discussion about this proposal. First, that we need to figure out what portion of such a system it is reasonable to standardize in a document published by the committee (whether the Standard or a Standing Document), versus some other forum. Second, people were hesitant about giving compilers build system-like responsibilities the way this proposal does, and suggested instead a cleaner delineation between the roles of the two. Finally, the proposal focuses on source distribution because binary distribution is a tougher problem to solve due to ABI issues; people felt it was worth trying to address binary distribution nonetheless.

Next Meeting

The next meeting of the Committee will be in Oulu, Finland, the week of June 20th, 2016.

Conclusion

For the first time, we have a pretty good idea of what’s going to be in C++17. While it may not be everything everyone wanted, it’s still shaping up to be a pretty solid release of the language, and will be accompanied by important Technical Specifications that round it out, such as Concepts, Ranges, Modules, and Coroutines.

The level of participation in C++ standardization is the highest it’s been in recent memory (and possibly ever), and I’m excited about continue to observe, participate in, and report on it. There will be two more meetings this year – stay tuned for further reports!

Other Trip Reports

Herb Sutter’s trip report is a good read as well, giving more in-depth background on the standardization process and the role of Technical Specifications.

Advertisements

About botondballo

I am a recent graduate of the University of Toronto, with a degree in Computer Science. I currently work for Mozilla as a software engineer.
This entry was posted in c++, programming and tagged , , . Bookmark the permalink.

17 Responses to Trip Report: C++ Standards Meeting in Jacksonville, February 2016

  1. Daniel S says:

    Thanks a lot for this (very detailed) trip report, Botond. Always a pleasure to read.

  2. Yupei says:

    What about inline variables and guaranteeing copy elision ? Will they make C++ 17?

    • botondballo says:

      Yes! Somehow, I overlooked these. They are in the “expected to be voted into C++17 in Oulu” category; I updated the post to list them. Thanks for catching that!

  3. Given the detail in this report and the coverage of the various subgroups, it seems evident that you do actually have the ability to be in more than one place at a time and your claims to the contrary are but a thinly veiled attempt to hide your superhuman powers!

    Nice job 🙂

  4. tomaszkam says:

    For the Return type deduction and SFINAE (btw. the link is broken), I am not proposing to perform SFINAE on function body, as your description suggested. I mean that the following:
    template auto f(T x) { return x == “2”; }
    template auto f(T x) { return x == 2; }
    will still be ambiguous.

    What my paper changes is that if return type deduction is triggered in function signature context, then failure in it would be treated as SFINAEable error. The example function would be:
    template
    void foo(F f, T t) -> decltype(f(t)) { return f(t); }
    For above expression:
    foo([](auto x) { return x == 2; }, “ala”);
    Requires lambda body to be instantiated during overload resolution today, but any failure in such in-satiation leads to hard error. Such scoped changes does not introduce the need for extended mangling to introduce new expression or cause new instantiations to be performed.

  5. Andreas Pfaffenbichler says:

    Impressive in multiple ways: the tremendious work of the committee and this comprehensive and enlightening report!
    Thank you very much!

  6. jmckesson says:

    I asked this over on Herb’s blog, but my main focus was kind of missed.

    With FileSystem going into the standard, has anyone been looking at actually integrating it properly into C++? That is, being able to use a `path` object directly to open streams, as well as with older C-based `fopen` and the like? Because without that, it would be very difficult to use `path` for file processing in a cross-platform way. The underlying type is implementation-defined, and you can’t shove a UTF-8 string into the Windows version of the standard library and load a file that uses Unicode characters. It must be a UTF-16 string for that to work.

    So is anyone actively working on fixing this current incongruity in C++?

  7. Sebastian says:

    Hi Botond,

    first of all thanks for summarizing the meeting to comprehensively. I really like your trip reports and have been following them for a while now. I have also been maintaining a small answer on SO (http://stackoverflow.com/a/25621565/474034) that summarizes the current state of the module proposal for other interested users.

    As I am writing a lot of C code, I am particularly interested in the following question: Do you know if there a any plans to make the module specification available for the C language as well? When I look on the Clang Module page (http://clang.llvm.org/docs/Modules.html) the Clang team’s interpretation of modules seems to include the whole C language family not only C++, but so far, from reading the drafts published by WG21, I could not find any indicator that modules are going to work for C. The talk is always only about C++.

    Could you maybe clarify if there are any plans for modules with respect to C in a reply or maybe in a separate blog post? Given the issues with the current header system and the advantages that come with a module system (particularly with respect to tool support and compilation speed) I am still hoping that there will also be a solution for C at some point in time.

    • botondballo says:

      C is standardized by a different committee, WG14; so for modules to work in standard C, WG14 would have to standardize the relevant subset of the C++ modules proposal. I’m afraid I don’t know much about what goes on in WG14, so I can’t say whether that’s being worked on. Of course, regardless of whether it’s standardized, implementations can support modules in C as an extension, which is what clang looks like it’s doing.

  8. Sebastian says:

    Hi Botond,

    thank you very much for your reply. I will keep an eye on WG14 to see if they will come up with anything. From what I have seen so far plans for the next C standard C2X have not yet progressed very far.

  9. Pingback: Trip Report: C++ Standards Meeting in Oulu, June 2016 | There's Waldo!

  10. Pingback: Trip Report: C++ Standards Meeting in Oulu, June 2016 | There's Waldo!

  11. Pingback: C++17, Genèse d’une version mineure – Open Romandie

  12. Pingback: Trip Report: C++ Standards Meeting in Issaquah, November 2016 | There's Waldo!

  13. Pingback: C++17 fixe l’ordre d’Ă©valuation des expressions – Open Romandie

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s