Trip Report: Spring ISO C++ Meeting in Tokyo, Japan

Last week, I attended the spring 2024 meeting of the ISO C++ standardization committee in Tokyo, Japan. This was the third meeting for the upcoming C++26 standard and my first meeting as assistant chair of SG 9, the study group for ranges.

I started the week on Monday in LEWG, the working group for the C++ standard library design. After the usual papers adding/extending std::format (Victor Zverovich keeps us busy), we approved a proposal that adds thread attributes, and reviewed the library parts of P2900 contracts. LEWG being LEWG, we mostly complained about the names (std::contracts::contract_violation has too many contracts in it), but overall liked it. However, contracts are a language feature, and the real controversy was over at EWG, the language design group. In particular, what happens if you have undefined behavior in a precondition? Consider the following example:

std::string_view slice(std::string_view str, int pos, int length)
	pre (0 <= pos && pos <= std::ssize(str) && 0 <= length && pos + length <= std::ssize(str))
{
	return std::string_view(str.data() + pos, str.data() + pos + length);
}
A slicing function for std::string_view using signed integers for demonstration purposes.

An integer overflow of pos + length in the precondition is undefined behavior. Some argue that this should instead be well-defined and lead to a precondition violation. While this would be nice and can lead to a general "safe mode" of C++ which could (and should!) be usable outside of contracts as well, I don't see how it can be worked out before C++26. I'd much rather have contracts with undefined behavior in C++26 then delaying it even further. The nice thing about undefined behavior is that it can be always well-specified later.

I then spent Tuesday and Wednesday (co-)chairing SG 9, the ranges study group. We mostly gave feedback on minor features like std::ranges::cache_last or a rangified version of lexicographical_compare_three_way, but also started looking at parallel range algorithms to allow trivial parallelization using C++17's execution policies. While certainly a nice feature, it worsens the combinatorial explosion of algorithm combinations. For each algorithm, like for_each or transform, we already have

  • a std overload taking iterators std::for_each(begin, end, f),
  • a std overload taking iterators and execution policy std::for_each(std::execution::par, begin, end, f),
  • a std::ranges overload taking iterator and sentinel std::ranges::for_each(begin, end, f), and
  • a std::ranges overload taking a range std::ranges::for_each(rng, f).

P3179 wants to add

  • a std::ranges overload taking iterator, sentinel, and execution policy std::ranges::for_each(std::execution::par, begin, end, f) and
  • a std::ranges overload taking a range and execution policy std::ranges::for_each(std::execution::par, rng, f).

In addition, there is also work on asynchronous algorithms using senders/receivers. That could mean up to eight copies of each standard library algorithm! I'm sure glad that I'm not a standard library implementer.

On Thursday and Friday, I cycled between LEWG, SG 21 (contracts), and SG 7 (reflection). P2996's version of reflection is actually on track for C++26 with implementations ready to go, which is really exciting.

I want to end by highlighting two proposals whose discussion I've missed but really want. The first is P2786 which enables "relocation". By specifying a trivially_relocatable class attribute, you can tell the compiler that you can optimize a destructive move (which is normally done by calling the move constructor on the destination followed by the destructor on the source) to a call to std::memcpy. This can make operations like std::vector::reserve more efficient. The paper is on track for C++26 after being forwarded to wording review by EWG.

The second is P2822 which allows control over ADL. By default, ADL looks in the enclosing namespace of the class as well as the namespaces of base classes and template arguments. This is often undesired: only a select few functions like operator overloads or swap should ever be called using ADL. At think-cell, we thus put all types into a separate sub-namespace containing only the functions that should be visible via ADL. P2822 allows you to specify the set of namespaces visible to ADL using a namespace(A, B, C) specifier on the class declaration. In particular, if you write namespace(), no namespace is visible via ADL and only hidden-friends are found. This is the way it should have worked in the first place! The paper was seen for the very first time by EWGI, the incubator for new language features, which forwarded it to EWG. I really hope it makes it into C++26, so I can get rid of all nested namespaces in our codebase.

Note that as C++ keeps getting the defaults wrong, we will soon not only write function declarations with a ton of modifiers

[[nodiscard]] constexpr auto foo();

but also classes...

class foo final trivially_relocatable namespace() {
  …
};
— by Jonathan Müller

Do you have feedback? Send us a message at devblog@think-cell.com !

Sign up for blog updates

Don't miss out on new posts! Sign up to receive a notification whenever we publish a new article.

Just submit your email address below. Be assured that we will not forward your email address to any third party.

Please refer to our privacy policy on how we protect your personal data.

Share