Summary / TL;DR
Project
What’s in it?
Status
C++17
See below
Committee Draft published; final publication on track for 2017
Filesystems TS
Standard filesystem interface
Published! Part of C++17
Library Fundamentals TS v1
optional, any, string_view and more
Published! Part of C++17
Library Fundamentals TS v2
source code information capture and various utilities
Voted for publication!
Concepts (“Lite”) TS
Constrained templates
Published! Not part of C++17
Parallelism TS v1
Parallel versions of STL algorithms
Published! Part of C++17
Parallelism TS v2
Task blocks, library vector types and algorithms, context tokens (maybe), and more
Under active development
Transactional Memory TS
Transaction support
Published! Not part of C++17
Concurrency TS v1
future.then(), latches and barriers, atomic smart pointers
Published! Not part of C++17
Concurrency TS v2
TBD. Exploring synchronic types, atomic views, concurrent data structures, sycnhronized output streams. Executors to go into a separate TS.
Under active development
Networking TS
Sockets library based on Boost.ASIO
Voted for balloting by national standards bodies
Ranges TS
Range-based algorithms and views
Voted for balloting by national standards bodies
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 part of C++17.
Graphics TS
2D drawing API
In design review stage. No new progress since last meeting.
Coroutines TS
Resumable functions
First revision will reflect Microsoft’s await design. Other approaches may be pursued in subsequent iterations. Not part of C++17.
Reflection
Code introspection and (later) reification mechanisms
Introspection proposal undergoing design review; likely to target a future TS
Contracts
Preconditions, postconditions, and assertions
In design review stage. No new progress since last meeting.
Introduction
Last week I attended a meeting of the ISO C++ Standards Committee (also known as WG21) in Issaquah, Washington (near Seattle). This was the third and final committee meeting in 2016; you can find my reports on previous meetings here (February 2016, Jacksonville) and here (June 2016, Oulu), and earlier ones linked from those. These reports, particularly the Oulu one, provide useful context for this post.
This meeting was heavily focused on C++17, with a secondary focus on in-progress Technical Specifications, and looking forward to C++20.
At the end of the last meeting, the C++17 Committee Draft (CD) – a first feature-complete draft of the C++17 spec – was sent out for comment from national standards bodies. The comment period concluded prior to this meeting, and as such, the main order of business at this meeting was to go through the comments and address them.
Note that, while the committee is obligated to respond to each comment, it is not obligated to accept the proposed resolution of the comment (if it has one); “there was no consensus for a change” is an acceptable response. (Technically, if a national standards body is unhappy with the response to their comment, it can vote “no” on the final standard, but this practically never happens; the prevailing practice is to respect the consensus of the committee.)
Addressing the CD comments is a process that typically takes two meetings. Indeed, the committee did not get through all of them this meeting; resolution of the comments will continue at the next meeting, at which point a revised draft, now labelled Draft International Standard (DIS) will be published, and sent out for a second round of comments.
C++17
Since the C++17 CD is supposed to be feature-complete, no new features were voted into C++17 at this meeting. See my Oulu report (and things linked from there) for features that were voted into C++17 at previous meetings.
However, some changes to C++17 (that don’t qualify as new features, but rather tweaks or bugfixes to existing features) were voted into C++17 at this meeting, mostly in response to national body comments.
Changes voted into C++17 at this meeting
Language:
Removing deprecated exception specifications. This was slated to be voted in at the last meeting, but the library group ran out of time before completing its review. Now that they have, it was voted in.
Pack expansions in using-declarations. This is technically a new feature, which was supposed to be accepted into the Committee Draft in Oulu, and just missed the boat due to a procedural error, so an exception was made to allow it into the CD now.
Various tweaks to class template argument deduction in response to naional body comments
Various tweaks to decomposition declarations (formerly known as “structured bindings”) and other language features in response to national body comments
Library:
constexpr for std::char_trais
Literal suffixes for basic_string_view
constexpr for std::chrono
Tweaks to atomic memory ordering
Structured bindings for node_handles
Two tweaks to std::shared_ptr
Revisiting in-place tag types for std::any, std::optional, and std::variant
Disallowing references, incomplete types, and arrays in std::variants, and empty variants
Updating restrictions on exception handling
Update to the treament of exceptions escaping from a parallel algorithm
shared_ptr::use_count() and unique()
Tweaks to the mechanism of std::hash. This allows us to express semantics such as “you can hash std::optional
if you can hash T“.
It’s worth observing that some of these library changes involve taking language changes previously accepted into the CD, such as structured bindings, and making use of them in the library (for example, adding structured bindings for node_handle). Since these types of library changes naturally “lag behind” the corresponding language changes, there have been requests to close the door to new language features earlier than for library features, to give the library time to catch up. No formal decision along these lines has been made, but authors of language features have been asked to give thorough consideration to library impact for future proposals.
Proposed changes to C++17 that were rejected
One notable change to C++17 that was proposed at this meeting was rejected: this introduction of byte type whose intended use was unambiguously “a unit of storage” rather than “a character” or “an 8-bit integer” (which is not true of existing byte-like types such as char or unsigned char). The proposal involved the definition of a library type named std::byte (defined as an enum class), and core wording changes to grant the type special treatment, such as allowing the construction of an arbitrary object into a suitable-sized array of byte, as is currently allowed for char (this treatment is relied upon by the implementations of containers such as std::vector that allocate a block of memory some but not all of which contains constructed objects). The long-term hope is for std::byte to replace char as the type that gets this special treatment, and eventually deprecate (and later) remove it for char.
The proposal to add this type to C++17 failed to gain consensus. This was partly because we’re late into the process (this would have had to be another exception to the “no new features beyond the CD” rule), but also because there were objections to naming the type “byte”, on the basis that there is a lot of existing code out there that uses this name, some of it for purposes other than the one intended in this proposal (for example, as an 8-bit integer type).
It seemed like there is still a chance for this proposal to gain consensus with the type’s name changed form “byte” to something else, but this is unlikely to happen for C++17.
Technical Specifications
In spite of the heavy focus on addressing comments on the C++17 CD, notable progress was also made on Technical Specifications (TS) at this meeting.
The procedure for publishing a Technical Specification is as follows:
Once it has completed design and wording review and is considered to be in good enough shape, a Preliminary Draft Technical Specification (PDTS) is published, and sent out for comment by national standards bodies.
The comments are reviewed and addressed (much like comments on a C++ Committee Draft)
The revised Technical Specification is sent out for final publication. (Optionally, if the committee believes the TS is not ready for publication and needs a second round of comments, it can publish a Draft Technical Specification (DTS) and send it out for comment, and only subsequently publish the final TS. However, this generally hasn’t been necessary.)
At this meeting, two TS’s were approved for being sent out for a PDTS ballot, and one was approved for final publication.
Library Fundamentals TS v2
The Library Fundamentals TS v2 had previously been sent out for its PDTS ballot, and the library working groups had been hard at work addressing the comments over the past few meetings.
At this meeting, the process was finally completed. With a few final changes being voted into the TS (a tweak to the searchers interface and miscellaneous other fixes), the TS was approved for final publication!
Ranges TS
The Ranges TS has passed its initial wording review. It picked up a couple of changes, and was approved to be sent out for its PDTS ballot!
The Ranges TS is unique in that it is, so far, the only TS to have a dependency on another one – the Concepts TS. There is nothing wrong with this; the only caveat is that, obviously, the Ranges TS can only be merged into the C++ standard itself after (or together with) the Concepts TS. Over time, I expect other TS’s in the future to be in a similar situation.
The Ranges TS is also notable for containing the beginnings of what could end up being a “standard library v2” – a refresh of the C++ standard library that uses Concepts and makes some (but not a gratuitous amount) of breaking changes compared to the current standard library. I’m excited to see it move forward!
Networking TS
The Networking TS has also passed its initial wording review. It, too, picked up a couple of changes, and was approved to be sent out for its PDTS ballot!
I’m also quite excited to see the Networking TS move forward. In addition to providing a socket library for C++, it defines foundational abstractions for asynchronous programming in C++. Its design has influenced, and will continue to influence, C++ proposals in other areas such as concurrency and (perhaps at some point in the future) input handling.
Coroutines TS
The Coroutines TS contains the co_await proposal, based on Microsoft’s original design.
As mentioned previously, there are efforts underway to standardize a proposal for a different, stackful flavour of coroutines, as well as an exploratory effort to unify the two flavours under a common syntax. These proposals, however, are not currently slated to target the Coroutines TS. They may instead target a different TS (and if a unified syntax emerges, it could be that syntax, rather than the one in the Coroutines TS, that’s ultimately merged into the C++ standard).
In any case, the Coroutines TS is currently moving forward as-is. A proposal to send it out for its PDTS ballot came up for a vote at this meeting, but failed to gain consensus, mostly on the basis that there has not been time for a sufficiently thorough review of the core language wording parts. Such review is expected to be completed by the next meeting, at which time – if all goes well – it could be sent out for its PDTS ballot.
Concepts TS
To recap the status of the Concepts TS: it was published last year, not merged into C++17, and now has a Working Draft that’s available to accept changes. Whether that Working Draft will eventually be published as a Concepts TS v2, or merged directly into C++20, remains to be decided.
One change was voted into the Concepts Working Draft at this meeting: a proposal to allow a requires-expression to appear in any expression context, not just in a concept definition. This is useful for e.g. using a requires-expression in a static_assert.
There is some uncertainty regarding the longer-term future direction of Concepts. The following areas seem to be somewhat contentious:
Definition checking, which I wrote about in a previous post. Definition checking is a feature that was part of the original C++0x Concepts design, and which has long been promised as an eventual extension to the current Concepts Lite design. However, there doesn’t seem to be consensus on how amenable the Concepts Lite design is to definition checking, or at least, what quality of definition checking it is amenable to. In my view, for example, if the definition checking doesn’t have the crucial property that “if a template definition passes the check, then all instantiations of it with arguments that satisfy the constraints will be successful”, then there is little point to the whole exercise. (It’s worth noting that Rust has the equivalent of Concepts (called traits in Rust), and the equivalent of definition checking (the checking that’s performed on generic parameters with trait bounds, to ensure that they’re not used in ways other than what’s allowed by the trait). Definition checking in Rust does have the property I mentioned above – this is why in Rust we don’t see the horrifying long compiler errors with instantiation backtraces that we’ve come to dread in C++.)
The proliferation of concept definition syntaxes. The “abbreviated function template” notation (where you get to write a function template that looks like a non-template function, using concept names in place of type names) in particular seems to be somewhat controversial. Allowing this syntax was one of the reasons the proposed merger of the Concepts TS into C++17 was rejected (the other major reason being insufficient implementation and use experience). Based on my conversations with other committee members, I get the impression that this objection is likely to persist the next time Concepts comes up for a vote (for example, for merger into C++20).
Modules TS
The Modules TS has a working paper, which largely reflects Microsoft’s design. It is under active wording review.
The implementers of Modules in clang have a proposal for some design changes based on their implementation experience. It is unclear at this point whether these changes, if approved, would go into the initial Modules TS; some of them, particularly the more controversial ones such as the support for exporting macros, may end up targeting a second revision of the Modules TS instead.
Parallelism TS v2
The Parallelism TS v2 is making good progress. It’s expected to contain task blocks, library vector types and algorithms, and perhaps context tokens. The TS should be ready for a PDTS ballot once wording review of the vector proposal is completed.
Concurrency TS v2
The Concurrency TS v2 (doesn’t have a working draft yet) is also making progress. It’s expected to contain a synchronized output stream facility, atomic_view, a synchronized value abstraction, queues, and counters, possibly among other things.
Executors, which have originally been slated for Concurrency TS v1 but got bogged down in lengthy design discussions over the years, have made notable progress at this meeting: for the first time, the various parties agreed on a unified proposal! This may end up targeting a separate Executors TS, so it doesn’t hold back the Concurrency TS v2.
The chair of SG 1 (the Study Group concerned with parallelism and concurrency) remarked that splitting proposals into “parallelism” and “concurrency” buckets seems to be less useful as time goes on and some topics, such as executors, concern both. It’s possible that these two series of TS’s will be folded together into a single series in the future, released more frequently.
Future Technical Specifications
In addition to the above already-inflight Technical Specifications, a number of new ones are planned for the future.
Reflection
The Reflection Study Group (SG 7) has been reviewing a proposal for static introspection (design, specification). At this meeting, the proposal was approved by SG 7, and sent onward for review by the Evolution and Library Evolution working groups starting at the next meeting. The ship vehicle will be determined by these working groups, but it’s expected to be a TS (the other possibility is C++20).
I write about SG 7’s meeting this week in more detail below.
Numerics
The Numerics Study Group (SG 6) is planning a Numerics TS that will contain a variety of facilities, as outlined here. The initial draft of the TS will likely just be a concatenation of the various individual proposals (which are being developed by various authors; see proposals with “Numerics” as the audience in the list here); the SG 6 chair hopes such an initial draft will be produced at the next meeting.
There was also a proposal to have a separate Technical Specification concerning random number generation, which received no objections.
Graphics
The Graphics TS, which proposes to standardize a set of 2D graphics primitives inspired by cairo, is still under design review. No time was spent reviewing the proposal at this meeting, so there is no new progress to report compared to last meeting.
Evolution Working Group
As usual, I spent my time in the Evolution Working Group (EWG), which spends its time evaluating and reviewing the design of proposed language features.
EWG did two things this week: first, it looked at national body comments on the C++17 CD which touched on the design of the language; and second, it looked at new language proposals, all of which were targeting C++20 or beyond (or a Technical Specification). I’ll talk about each in turn.
National Body Comments
The national body comments can be found in two documents: the official ones, and the late ones which therefore aren’t considered official, but were still looked at. I can’t link to individual comments directly, so I refer to them below by country and number, e.g. “US 1”.
A couple of procedural notes on comment processing:
When the C++17 CD was published, the document enjoyed the committee members’ consensus (otherwise it wouldn’t have been published). A change to it at this stage is only considered if it increases consensus, which is a high bar. Comments proposing changes which do not meet this high bar are disposed with the response “there was no consensus for a change”.
As the comments originate from a variety of authors, they sometimes ask for contradictory things (sometimes even within a single national body). In such cases, both options are considered, but again, a change is only made if it increases consensus (which is less likely in this scenario).
With that said, here are the comments that were looked at, grouped by the feature they concern:
Comments concerning class template argument deduction
Implicit deduction guides (GB 21, US 19, US 20)
To recap, class template argument deduction is a feature where template arguments for a class template can be deduced at construction time, instead of being explicitly specified, like so:
std::pair p(42, "waldo"s); // deduce std::pair
The deduction can be guided either by the constructors of the primary template, which are called implicit deduction guides in this context, or by explicit deduction guides, which are new constructs declared outside the template specifically for the purpose of guiding this sort of deduction.
The proposal authors analyzed the impact of this feature on the standard library and found that, while in many cases the existing constructors provide the desired deduction behaviour, in some other cases explicit guides would need to be added to complement or correct the behaviour of the implicit ones.
The comments listed here are concerned with what to do with implicit deduction guides in light of this library impact. One comment proposed removing them altogether, requiring explicit guides to be written in all cases; two others request tweaks to the behaviour of implicit guides.
EWG expressed a strong preference for keeping implicit deduction guides, but also for adding to the standard library whatever explicit guides are necessary to get desired deduction behaviour for standard library types.
The proposed behaviour tweaks were accepted. The first proposed that if, for a given constructor call, an explicit guide and an implicit guide are equally good matches, the explicit guide be preferred, instead of resulting in an ambiguity.
The second tweak was more interesting. It concerned the behaviour of a T&& parameter in a constructor of the primary template. Parameters of the form T&& have the unique property that they behave differently depending on whether or not T is deduced. If T is not deduced, they are always rvalue references (unless T is explicitly specified to be an lvalue reference type); if T is deduced, they are forwarding references (formerly called universal references), which may end up being lvalue or rvalue references. Now, if T is a template parameter of a class, and a constructor of that class has a T&& parameter, normally T is not deduced; but when the constructor is used as an implicit deduction guide, it is! As a result, the semantics of the parameter silently change. To avoid this, a tweak to the rules was made to ensure that the parameter retains its non-forwarding semantics in the implicit guide context.
Uniform initialization (FI 21, GB 13, US 94). These comments concerned the fact that, while the feature allows std::pair p(42, 43), std::pair p{42, 43}, and std::pair(42, 43), it does not allow std::pair{42, 43}. EWG agreed that this inconsistency should be fixed, although it did ask for a paper to be written, as specifying this was deemed sufficiently non-trivial.
Comments concerning expression evaluation order (CA 18, US 1, US 3, US 93, US 102, Late 4).
Several of these comments concerned the discrepancy in the order of evaluation between a @= b (where b is evaluated before a, for consistency with assignment) and a.operator=(b) (where a is evaluated before b, for consistency with member function calls). (Note that @ here is meant to stand in for any operator that can be combined with =, such as +=, -=, etc.) This issue was discussed previously, and the comments didn’t really bring any new information to the table; there was no consensus for a change.
Another comment asked to revisit the decision previously made to have the evaluation of function arguments with respect to each other be unsequenced, and instead evaluate them left-to-right. There was no consensus for a change here, either.
Comments concerning decomposition declarations (“structured bindings”)
Syntax – [] vs. {} (ES 2, FI 23, US 23, US 71, Late 9, Late 12). The original proposal for decomposition declarations used the syntax auto {a, b, c}; that was changed at the last meeting to auto [a, b, c]. This change was fairly controversial, and several comments asked to change it back to {} (while others encouraged keeping the []). There are technical arguments on both sides (the [] syntax can conflict with attributes once you start allowing nested decompositions; the {} syntax can conflict with uniform initialization if you throw Concepts into the mix and allow using a concept-name instead of auto), so in the end it’s largely a matter of taste. The clang implementers did report that they tried both, and found the ambiguities to be easier to work around with []. In the end, there was no consensus for a change, so the status quo ([] syntax) remains.
static, thread_local, constexpr, extern, and inline decomposition declarations (GB 16, GB 17, US 95). There was general agreement that allowing these modifiers is desirable, but the exact semantics need to be nailed down (for example, for constexpr is only the unnamed compound object constexpr, or the individual bindings too?), and EWG felt it was too late to do that for C++17. The comment authors were encouraged to come back with proposals for C++20.
Decomposition in init-captures (US 92). EWG considered this an extension, and was open to it, but not in the C++17 timeframe.
Discarding values in decomposition declarations (US 100). This was rejected as the topic had been previously discussed.
Decomposition declarations an arrays (GB 18). This comment pointed out an inconsistency where decomposition declarations copy arrays by value, but the underlying mechanism used to specify them is template argument deduction, which decays arrays to pointers. The suggestion was to remove the special-case behaviour for arrays in decomposition declaration. Others, however, defended the special case, arguing that not copying arrays by value is a mistake inherited from C, and new features like this are opportunities not to repeat such mistakes. There was no consensus for a change.
Explicit types in decomposition declarations (FI 3). This was a request (also accompanied by a paper) to allow explicitly specifying the type of the compound object (and/or of the individual bindings). (As a strawman syntax, consider auto [a : T, b : U, c : V] = expr, where the decomposition is ill-formed if the types of a, b, and c do not match T, U, and V, respectively). EWG was open to an extension along these lines, but not in the C++17 timeframe.
Decomposition declarations in parentheses (FI 20). This was a request to allow the form auto [a, b, c](expr) in addition to auto [a, b, c] = expr and auto [a, b, c]{expr}; that was approved.
When are get() functions called in decomposition declarations? (Late 3). This comment concerned the case where type being decomposed is neither an aggregate strucure nor an array, but a tuple-like user-defined type where the individual bindings are extracted using get(). The specification wasn’t clear as to whether these get() calls occur “eagerly” (i.e. at the point of decomposition) or “lazily” (at the point of use of the individual bindings, which can potentially mean calling get() multiple times for a given binding). EWG decided in favour of “lazy”, because it enabled certain use cases (for example, decomposing a type such as a status/value pair where it’s only legal to access the value if the status has a certain value), while types that do nontrivial work in get() (thus making multiple calls expensive) seem rare.
Comments concerning default comparisons (ES 7, RU 5, US 5, US 69, Late 7, Late 14). As you’ll recall from my Oulu report, default comparisons failed to achieve consensus to go into C++17. These comments were an attempt to save the feature by putting a less-controversial subset into C++17. There were three specific proposals for doing so on the table:
The first proposal concerned only generating operator!= from operator==, and operator>, operator, and operator>= from operator; operator== and operator themselves still have to be manually defined (although automatic generation of those would be a compatible extension). The proposal also contained a new mechanism for “generating” these derived operators: rather than having the compiler generate what behave like new overloaded functions, expressions of the form a != b are reinterpreted/rewritten to be of the form !(a == b) if a suitable operator!= cannot be found (and similarly for the operators derived from operator). It was pointed out that this mechanism, like the mechanisms underlying some of the previous proposals, suffers from the slicing problem: if a base class defines both operator== and operator!= (as a class in existing code might) while a derived class defines only operator== (as a class written with this proposal in mind might), then an expression of the form a != b, where a and b are objects of the derived class, will call the base’s operator!= because there is a matching operator!= (and as a result, any data members in the derived class will not participate in the comparison).
The second proposal concerned default generation of operator== (but not operator), based on the following simple rule: if operator= is auto-generated, so is operator==, the idea being that the semantics of assignment and equality are fundamentally entwined (if you make a copy of something, you expect the result to behave the same / be equal). The idea had support, but some people were still concerned about having even == be auto-generated without an explicit opt-in.
The third proposal concerned adding an opt-in syntax for asking for any of the comparison operators to be generated by declaring them as = default. This has been previously proposed, and people’s concerns largely mirrored those at the previous discussion.
None of these proposals achieved consensus for C++17. The main reason had to do with another proposal concerning comparison, which wasn’t slated for C++17. This proposal tried to bring some mathematical rigour to comparison in C++, by observing that some types were totally ordered, while others were only weakly or partially ordered. The proposal suggested an API, based on functions rather than operators, to perform tasks such as a three-way comparison of two objects of a totally ordered type. This got people thinking that perhaps down the line, we could build == and on top of a three-way comparison primitive (for totally ordered types); this would, for example, enable us to generate an operator that’s more efficient than what’s possible today. People liked this future direction, and in light of it, each of the above proposals seemed like a half-baked attempt to rush something into C++17, so the group settled on getting this right for C++20 instead.
Comments concerning concepts (US 2). While Concepts was not accepted into C++17, a comment asked that a subset of the proposal, consisting of requires-clauses and requires-expressions, be accepted. EWG had no consensus for making a change here; it was felt that the proposed subset was sufficiently substantial that several of the reasons not to put Concepts as a whole into C++17 still applied to them.
constexpr static members of enclosing class type (US 24). This comment concerned the fact that a class cannot contain a constexpr static data member whose type is that class type itself. The comment proposed making this work by deferring the processing of such a data member’s initializer (which is what requires the type to be complete) until the end of the class definition. Doing this in general (i.e. for all constexpr static data members) would be a breaking change, since declarations that occur later inside the class definition can use the data member. However, doing it for a subset of cases, that are currently ill-formed, would work, and some ideas were bounced around about what such a subset could be. (A simple one is “when the data member’s type matches the class type exactly”, but we might be able to do better and cover some cases where the data member’s type isn’t the class type exactly, but still requires the class type to be complete.) EWG encouraged people to work out the details and come back with a paper.
There was a comment requesting to rename __has_include (US 104) to have a “less ugly” name, such as has__include. This was rejected because there is existing practice using the name __has_include
New Language Features
Here are the post-C++17 features that EWG looked at, categorized into the usual “accepted”, “further work encouraged”, and “rejected” categories:
Accepted proposals:
template keyword in unqualified-ids. This concerns a parsing quirk of C++. When the compiler encounters a name followed by a left angle bracket, such as foo, it needs to decide whether the starts a template argument list, or is a less-than operator. It decides this by looking up the name foo: if it names a template (in standardese, if it’s a template-name), the is assumed to start a template argument list, otherwise it’s assumed to be a less-than operator. This heuristic often works, but sometimes falls down: if you have code like foo
(arg)
, where foo is a template function that’s not visible in the current scope, but rather found by argument-dependent lookup (by being in one of the associated namespaces of arg), then at the time the compiler looks up foo, no template-name is found, so the construct is parsed wrongly. This proposal allows the programmer to rectify that by writing template f
(arg)
; think of the template as meaning “I promise the name that follows will resolve to a template”. This mirrors the existing use of the template keyword when naming a template nested inside a dependent type, such as Base
::template nested
. Some people had reservations about this proposal resulting in programmers sprinkling template about rather liberally, but the proposal passed nonetheless.
Allowing lambdas in unevaluated contexts such as inside decltype(). This came up previously, and the feedback was “we like it, but please make sure appropriate restrictions are in place such that the bodies of lambdas never need to be mangled into a signature”. The author revised the proposal to put in place appropriate restrictions. EWG asked for one more tweak – to exclude the body of the lambda expression from the “immediate context” of an instantiation for SFINAE purposes – and with that tweak passed the proposal.
Familiar template syntax for generic lambdas. This will allow writing generic lambdas like []
(T arg)
in addition to [](auto arg). The advantage of the first form is that it can express things the second form cannot, such as []
(T a, T b)
. The two forms can be combined as in []
(T a, auto b)
; in such a case, the invented template parameters (for the autos) are appended to the explicitly declared ones.
Allowing the lambda capture [=, this]. The semantics are the same as [=], but this form emphasizes that the we are just capturing the this pointer, and not the pointed-to object by value (syntax for the latter, [*this], was previously added to C++17). (The question of allowing this change into C++17 came up, but there wasn’t a strong enough consensus for it.)
A class for status and optional value. This is a library proposal, meant to be an error handling mechanism for cases where throwing an exception is not possible or not ideal (for example because the absence of a value is commonplace, and exceptions would impose undue overhead). The proposal sailed by EWG on its way to LEWG (the Library Evolution Working Group), mostly to see if EWG was OK with standardizing an alternative to exceptions (it was).
Benchmarking primitives. This can be considered a follow-up to the timing barriers proposal which was discussed at the previous meeting and revealed to be unimplementable. The paper proposes two benchmarking primitives which are implementable (with equivalents being widely used in production benchmarking libraries today): touch(), which pretends to write to all bits of an object (to prevent the compiler from using information it might have had about the previous value of the object for optimizations), and keep(), which pretends to read all bits of an object (to prevent the compiler from optimizing the object away on account on no one looking at it). They would be library functions, in the namespace std::benchmark, but the compiler would impart special meaning to them. The choice of names was disputed, but that was left for LEWG to bikeshed. (There was a suggestion to put the functions in a namespace other than std::benchmark on the basis that they could be useful for other applications, such as secure zeroing. However, it was pointed out that these functions are not sufficient for secure zeroing, as the hardware can also play shenanigans such as eliding writes.)
Proposals for which further work is encouraged:
Querying the alignment of an object. C++11 gave us an alignas specifier to use on a type or a variable, and an alignof operator to query the alignment of a type; missing is an alignof operator to query the alignment of a variable. In practice implementations already provide this, and this proposal thus standardizes existing practice. The discussion centred around whether alignof x (without parentheses) should be allowed, like sizeof x (the consensus was “no”), and whether alignof((x)) (with double parentheses) should return the alignment of the type of x rather than the alignment of the variable x (mirroring the behaviour of decltype, where decltype(x) returns the declared type of the entity x, whereas decltype((x)) returns the type of the expression x; the consensus here was also “no”).
Pattern matching for types. This is a compile-time pattern matching mechanism for types in the same vein as the runtime pattern matching mechanism for values that was proposed earlier. It complements if constexpr in a similar way to how switch (or the run-time pattern matching) complements if. The proposed construct would be usable in a statement, expression, or type context, and would have “best match” semantics (where, if multiple patterns match, the best match is chosen). EWG encouraged further work on the proposal, including exploring the possibility of matching on non-type values and multiple types (you can accomplish either with the current proposal using std::integral_constant and std::tuple, respectively, but first-class support might be nicer).
Bitfield default member initializers. The initial version of this proposal allowed the naive syntax, int x : width = init;, by adding some disambiguation rules. At the time, EWG thought the rules would be hard to teach, and asked for a new syntax instead. The proposal was revised, suggesting the syntax int x : width : = init; (notice the extra colon). EWG didn’t like this syntax, nor any of the proposed alternatives (another was int x:[width] = init;), and instead settled on allowing the naive syntax after all, but with different disambiguation rules that avoid any code breakage, at the expense of forcing people who add initializers to their bitfields to sprinkle parentheses in more cases.
A unified API for suspend-by-call and suspend-by-return coroutines. As I’ve described in previous posts, two flavours of coroutines are being proposed for standardization in C++ – ones with suspend-by-return semantics (a.k.a. “stackless” coroutines), and ones with suspend-by-call semantics (a.k.a. “stackful” coroutines) – and EWG has expressed an interest in exploring whether the two models can be unified under a common syntax. This is a concrete proposal for such a syntax, taking the form of a library API. Feedback from EWG fell into two main categories. First, there was a question of whether this API is at the appropriate level of abstraction; some felt that a higher-level API that includes the notion of a scheduler may be better (the proposal author posited that such an API could be built on top of this one). Second, some felt that it was starting to become clear that a unified API necessarily comes at the cost of some overhead – depending on the implementation strategy, either compile- and link-time overhead, or runtime overhead – and in light of this, the unification may not be worth it. To elaborate on this point: suspend-by-call semantics gives you certain freedoms that suspend-by-return does not – for example, the ability to write code that’s unaware of whether the functions it calls suspend. The proposed unified API preserves these freedoms, even if a suspend-by-return implementation is used, and this is the source of the overhead.
Operator dot, for which there are two proposals on the table.
The original proposal almost made C++17, but was booted at the last minute when people realized it failed to specify how operator dot interacts with other user-defined conversions. The authors now have an updated proposal that addresses these interactions.
Meanwhile, there’s another proposal, which aims to achieve the same goals via an inheritance-like mechanism. Instead of having the wrapper class declare an operator. that returns the wrapped class, the wrapper class declares the wrapped class as a delegate base, using the syntax class Wrapper : using Wrapped, and provides a conversion operator to access the wrapped object (which is stored as a member, or obtained from somewhere else; it’s not implicitly allocated as part of the wrapper object as it would be with real inheritance). The beauty behind this proposal is that the desired behaviour (“when you access a member of the wrapper object, it resolves to a member of the wrapped object if there is no such member in the wrapper object”) falls naturally out of the existing language rules for inheritance (lookup proceeds from the derived class scope to the base class scope), without having to invent new language rules. As a result, the proposal enjoyed a positive reception.
Both proposals will be discussed further at the next meeting.
Attributes for likely and unlikely branches. These would be standardized versions of the widely implemented __builtin_expect(condition, true) and __builtin_expect(condition, false) constructs, spelt [[likely]] condition and [[unlikely]] condition, and restricted to appearing in the condition of an if-statement. EWG liked the idea, but expressed a preference for putting the attribute on statements, such as if (condition) [[likely]] { ... }; this would then also generalize to other control flow constructs like while and switch.
Procedural function interfaces. This paper presents a theoretical framework for statically reasoning about the correctness of a program. Described as a “long-term feature”, the eventual goal is to annotate interfaces in a program with claims/assertions in such a way that a tool can statically prove aspects of the program’s correctness. There is some overlap with contracts: this proposal can be thought of as an alternative to contracts, with more ambitious goals; the author believes unification of the proposals is possible. A notable difference from existing proposals is that, rather than trying to express reasoning about a procedural program in language resembling mathematics, this seeks to “express the mathematical proof of a program’s correctness in a procedural way”.
Template deduction for nested classes. Currently, a type like A
::type
, where T is a template parameter, is a “non-deduced context”, meaning that if a function parameter’s type has this form, T cannot be deduced from the corresponding argument type. (Template argument deduction also comes up in the process of matching class template partial specializations, so this restriction means you also can’t specialize a template for, say, “vector
::iterator
for all T“). The main reason for this restriction is that in A
::type
, type can name either a nested class, or a typedef. If it names a typedef, the mapping from A
::type
to T may not be invertible (the typedef may resolve to the same type for different values of T), and even if it’s invertible, the inverse may not be computable (think complex metaprograms). However, if type names a nested class (and we make the case where an explicit or partial specialization makes it a typedef ill-formed), there is no problem, and this proposal suggests allowing that. EWG’s feedback was mainly positive, but a significant concern was raised: this proposal would make whether type names a nested class or a typedef part of A‘s API (since one is deducible and the other isn’t), something that wasn’t the case before. In particular, in a future world where this is allowed, the common refactoring of moving a nested class out to an outer level and replacing it with a typedef (this is commonly done when e.g. the enclosing class gains a template parameter, but the nested class doesn’t depend on it; in such cases, leaving the class nested creates unnecessary code bloat), becomes a breaking change. The author may have a way to address this using alias-templates; details with forthcome in a future revision of the proposal.
Pointers to reference members. It is currently invalid to form a pointer to a reference, which means you can’t have a pointer to a member if that member has reference type. This is inconvenient for the reflection proposal, which allows iterating over the data members of a class and getting a pointer to each; currently, such iteration has to skip over members of reference type. This proposal explores filling that hole by allowing pointers to reference members (without allowing pointers to references in general). EWG was open to the idea, but was concerned about this being a slippery slope and eventually opening the door to things like arrays of references. It was also pointed out that reflection has other options available to it. For example, some have proposed an unreflexpr operator which does the reverse of reflexpr: that is, given a meta-object representing an entity in a program (as obtained by reflexpr, or by navigating the meta-object graph starting from another meta-object obtained via reflexpr), unreflexpr gives you back that entity. This would avoid the need to form a pointer to a reference data member: given a meta-object representing the reference data member, you’d just call unreflexpr on it and use the result in places where you’d dereference the pointer.
Implicit move on return from local and function parameter rvalue references. This proposes that when a return-expression names a local or parameter variable of rvalue reference type, the return implicitly move it (currently, if you don’t use std::move, std::forward, or similar, a copy is made, because a named rvalue reference is still an lvalue). This would be a breaking change, but the breakage is likely to be limited to situations where the code is already buggy, or using move semantics improperly. EWG encouraged exploration of the scope of breakage in real-world code.
Rejected proposals:
Reconsidering literal operator templates for strings. This is a proposal to allow user-defined literal strings, such as "string"op, where op would get the characters of the string as arguments to a template
. This was originally rejected over compile-time performance concerns (processing many template arguments is expensive), and it was suggested that the proposal be revised with additional machinery for compile-time string processing (which would presumably address the performance concerns). This reincarnation of the proposal argued that literals of this form have a variety of uses unrelated to string processing, and as such requiring accompanying string processing machinery is overkill; however, the underlying performance concerns are still there, and as a result the proposal was still rejected in its current form. It was pointed out that a more promising approach might be to allow arrays (of built-in types such as characters) as non-type template parameters; as such, the string would be represented as a single template argument, alleviating many of the performance issues.
A char8_t type for UTF-8 character data, analogous to the existing char16_t and char32_t. This was previously proposed, albeit in less detail. The feedback was much the same as for the previous proposal: consult more with the Library Evolution Working Group, to see what approach for handling UTF-8 data is likely to actually gain traction.
Static class constructors. These would be blocks of code associated with classes that run once, during static initialization. EWG found this proposal to be insufficiently motivated, observing that at least some of the problems the proposal aims to solve are addressed by inline variables. It was also observed that the proposal doesn’t solve the issue of order-of-initialization of static objects across translation units; some consider solving that to be a blocker for other advancements in the area of static initialization.
Template argument deduction for non-terminal function parameter packs. The motivation here is to be able to write things like template
void foo(Args... args, Last last)
– basically, pattern-matching on a list of variadic arguments to separate and deal with the last one first. This is currently ill-formed, mostly because it’s unclear what the semantics should be in the presence of default arguments. EWG was disinclined to allow this, preferring instead to address the use case of “get the last element of a parameter pack” with a more general pack indexing facility (one such facility is reportedly in the works; no paper yet).
Other Working Groups
Having sat in EWG all week, I don’t have a lot of details to report about the proceedings of the other groups (such as Library Evolution or Concurrency) besides what I’ve already said above about the progress of Technical Specifications and other major features.
I do have a few brief updates on some Study Groups, though:
SG 7 (Reflection)
SG 7 had an evening session this meeting, during which it reviewed and gave its final blessing to the static introspection proposal, also known as reflexpr after the operator it introduces. This proposal will now make it way through the Evolution and Library Evolution groups starting next meeting.
Being a first pass at static introspection, the proposal has a well-defined scope, providing the ability to reflect over the data members and nested types of classes and the enumerators of enumerations, but not some other things such as members of namespaces. Reflecting over uninstantiated templates also isn’t supported, in part due to difficulty-of-implementation concerns (some implementations reportedly represent uninstantiated templates as “token soup”, only parsing them into declarations and such at instantiation time).
Notably, the proposal has a reference implementation based on clang.
The proposal currently represents the result of reflecting over an entity as a meta-object which, despite its name, is a type (keep in mind, this is compile-time reflection). A suggestion was made to use constexpr objects instead of types, in the same vein as the Boost Hana library. SG 7 was hesitant to commit to this idea right away, but encouraged a paper exploring it.
The facilities in this proposal are fairly low-level, and there is a room for an expressive reflection library to be layered on top. Such a reflection library could in turn use a set of standard metaprogramming facilities to build upon. SG 7 welcomes proposals along both of these lines as follow-ups to this proposal.
SG 13 (Human-Machine Interaction)
SG 13’s main work item, the Graphics TS, is working its way through the Library Evolution Working Group.
The group did meet for an evening session to discuss a revision of another proposal concerning input devices. I wasn’t able to attend, but I understand the main piece of feedback was to continue pursuing harmony with the asynchronous programming model of the Networking TS.
SG 14 (Game Development & Low-Latency Applications)
SG 14 didn’t meet this week, but has been holding regular out-of-band meetings, such as at GDC and CppCon, as well as teleconferences. The group’s mailing list is a good place to keep up with progress in the area.
Next Meeting
The next meeting of the Committee will be in Kona, Hawaii, the week of February 27th, 2017.
Conclusion
C++17 is turning out to be an exciting release of the language, with the most talked-about features including: on the language side, structured bindings, class template argument deduction, and if constexpr; on the library side, the filesystem facilities, and vocabulary types like variant and optional; and on the concurrency side, the parallel versions of the standard library algorithms.
This meeting was spent polishing this feature set in preparation for the C++17 standard’s official release next year.
Meanwhile, we are looking forward to an even more exciting C++20, which may contain features ranging from Concepts, Ranges, and Networking to Modules and Coroutines, with some of these already available as Technical Specifications, or soon to be.
I look forward to continuing to participate in and report on the standardization process!