ISO C++ Committee – Jacksonville 2018 trip report
This trip report is provided by Timur Doumler, who joined the CLion team in fall 2017. You might know him from his work on the JUCE framework, the Audio Developer Conference (ADC) organization, and dozens of talks about modern C++ at various conferences. Now working at JetBrains, Timur continues to be active on the C++ Committee. He is now mostly involved in the Evolution Working Group (EWG), the subgroup of the committee that reviews and approves the design of new core language features. This fits well into our strategy at JetBrains: we would like to be actively involved in the committee and feed our experience with implementing language features in our C++ tools back into the ongoing work on the evolution of the C++ language.
Timur:
From the 12th to the 17th of March 2018, I traveled to Jacksonville, Florida, to attend the ISO C++ Committee meeting as JetBrains’ representative on the committee. As always, it was a great week, packed with interesting discussions and decisions towards the upcoming C++20 standard. The committee is organized into several Working Groups with many sessions taking place in parallel, making it virtually impossible to keep track of everything. So I decided to focus on EWG and cover the most relevant discussions concerning language evolution.
Day 1: Structured bindings and concepts
On Monday, EWG looked at structured bindings. This feature was one of the last-minute additions to C++17, and now that people have started using it, new issues and ideas have come up. One particular issue that we discovered here at JetBrains while implementing structured bindings for CLion 2018.1 is the inconsistency between access rules in structured binding declarations and other kinds of declarations. C++17 currently defines that for structured bindings to bind to a class, all members have to be public. This surprisingly means that the following code is ill-formed:
class Foo { int a, b; void bar(const Foo& other) { auto [x, y] = other; // Error: 'a' and 'b' are private! // ... } };
but if you write out the offending line without structured bindings like this, the code compiles:
auto x = other.a, y = other.b;
We suggested a fix to remove the “public member” requirement from [dcl.struct.bind], and instead to apply the usual rules like in all other parts of the language, i.e. that the members are accessible from the context of the declaration. Coincidentally, this also makes our implementation in CLion much cleaner. The fix was approved and voted into C++20, and was further accepted as a defect report against C++17.
In EWG we then discussed various other papers proposing extensions to structured bindings, such as using them in conditions (like `if (auto [x, y] = result)`) and in generic lambda parameter declarations. However, we felt that none of those designs fit well enough into the language to be adopted.
The Monday evening session was spent on discussing various proposals for a “shorthand” or “terse” syntax for concepts. The most promising proposal currently is Herb Sutter’s P0745, proposing a new syntax with curly braces like `Iterable{T}`. But like all the other proposals, this one also had its issues, and more work is needed before the committee will have a consensus on any particular terse concept syntax.
Day 2: Let’s break some code!
Tuesday brought an unexpected turn of events. We were discussing inconsistencies between C++20’s new spaceship operator `<=>` and the old comparison operators `<`, `>`, `<=`, `>=`, `==`, `!=` when comparing certain built-in types. The resolution that gained consensus in EWG was to actually make changes to the old operators:
- Change `<`, `<=`, `>`, `>=`, `==`, `!=` such that they now give the correct mathematical meaning on arithmetic types, such that, for example, `static_assert(-1 > 1u);`, which would pass in C++17, will break in C++20.
- Deprecate two-way comparisons and arithmetic conversions between enumeration types and floating-point types.
- Deprecate two-way comparisons and arithmetic conversions between two distinct enumeration types.
- Deprecate two-way comparisons where both operators are of array type.
- Deprecate relational comparisons between function pointers.
This is an interesting outcome. On the one hand, this finally rids C++ of some old warts and annoying oddities that have been in the language for a long time. On the other hand, it breaks compatibility with C and C++98/11/14/17. Those changes have not yet been officially voted into C++20, and I expect some controversy around them.
Day 3: Coroutines and Modules
The rest of Tuesday as well as Wednesday we discussed the state of the Coroutines TS and Modules TS. For both TSes, there are quite a few papers pointing out various unresolved issues, and even counter-proposals with significant design changes. In their current state, there is no consensus yet in the committee to merge either of them into the C++20 working paper, and more work is needed. For coroutines, the expectation is that the issues can be resolved this year and we will get coroutines in C++20. For modules, the situation is much less clear, and a lot more work is still required to get them right. Right now it seems more likely that we will have to wait until C++23 for modules in the standard. A particularly controversial aspect is the open question of what is the most efficient way to deal with #include headers if they are mixed with modules.
Here at JetBrains, our main concern with modules is to make sure that – regardless of the exact syntax and semantics that will eventually make it into the standard – this feature will be toolable. Compiler vendors are developing intermediate representation formats for modularized C++ code, and the Modules TS does not specify whether (or how) the original C++ code will be accessible through those formats – for example, for features like code inspection and refactoring. It also seems unlikely at this point that compiler vendors will align their different formats. We are working with other committee members to make sure that we will end up with a compiler ecosystem for modularized code that will work well with IDEs and other C++ tools and can provide a great experience for C++ developers.
Day 4: Compile-time programming
On Thursday, two decisions stood out as exciting for the evolution of the language. The first was EWG’s approval of P0732: in C++20, we will be able to use class types as non-type template parameters. This will finally allow us to have useful compile-time strings, along the lines of
template <std::fixed_string Str> struct A {}; using hello_A = A<"hello">;
and many other use cases. Here’s the reason why this is so tricky: during template instantiation, the compiler needs to be able to efficiently decide whether two types are the same, by comparing the mangled typename (which includes the values of non-type template parameters). P0732 offers a solution to this problem: it mandates that class types that are used as non-type template parameters must have a defaulted (compiler-generated) `operator <=> ` guaranteeing strong equality.
The second milestone was EWG’s approval of P0784, allowing `constexpr` destructors and the use of `new` and `delete` in compile-time `constexpr` contexts. This opens the door to making `vector` and other standard containers `constexpr`, allowing them to be used for compile time metaprogramming.
Day 5: New Tooling Study Group
Friday was again an exciting day for JetBrains: the inaugural meeting of SG15, the new Tooling Study Group, took place. SG15 is a new platform in the C++ committee where vendors of IDEs and other tools can discuss challenges and future directions in C++ tooling, exchange ideas, and use their experience to inform the ongoing standardization work. This first meeting did not bring concrete results yet, but we had a great discussion about better aligning C++ build systems, package distribution, and dependency management, as well as using static analysis and refactoring to help users modernize their code.
Day 6: Plenary
Saturday was, as always, dedicated to the closing plenary session, where new wording is officially voted into the working draft for the next C++ standard. Here is a selection of features that successfully made the cut this time:
- relaxed rules on `typename`, removing the need to type it if it’s unambiguous in the given context
- allowing pack expansions in lambda init-captures
- [[likely]] and [[unlikely]] attributes to help optimization
- a new <version> header to replace the vacuous <ciso646>
- extending <chrono> to calendars and time zones
- mandating that
std::string::reserve
never shrinks (which also makesstd::string
more consistent withstd::vector
) - adding the very useful
std::span
– a bounds-safe view for sequences of objects
We also sent the Parallelism TS to ISO balloting, and decided to create a new Reflection TS working draft.
Overall, it was a very productive and eventful week. I believe that we are making really good progress towards C++20, and I am looking forward to the next committee meeting this June in Rapperswil, Switzerland.
JetBrains
The Drive to Develop