Trip Report: C++Now 2024

I attended C++Now for the fourth time last week, and it was definitely one of the best conferences I've ever been to. I don't think I've ever experienced so many amazing talks in a row, so I can only highlight some of the talks that are definitely worth watching.

C++, Rust, and safety

A talk absolutely everybody should watch is C++ should be C++ by David Sankel. It shouldn't be controversial that C++ has a bit of an image problem right now, given the legislative push for memory safety in programming languages and all the successor languages popping up. David argues that this isn't a big problem: There is already a proven, memory-safe systems programming language, Rust, and you should use it for safety-critical code. Programming languages are tools, and you should use the right tool for the job. Sometimes that's Rust, sometimes that's C++, and that's fine. The committee should instead focus on keeping C++ mostly as-is and look at ways to improve inter-op with other programming languages. I tend to agree with that.

If you violate a precondition in the standard library, it is undefined behavior, which is unsafe. People often complain about that; the Rust approach, where preconditions are checked and the program panics instead, is safe. However, an implementation is free to do whatever it wants in the presence of undefined behavior, including checking the precondition and aborting. And finally, somebody does: Users of libcxx can now enable a hardening mode in production to reliably check cheap preconditions. Louis Dionne presented the design in Security in C++ - Hardening techniques from the trenches. It has minimal runtime and code-size overhead, can be enabled/disabled separately for each translation unit, and is currently running in many production systems like Chrome. There really is no excuse anymore to ship code that can suffer from buffer overflows!

C++ at Google

Google does not use std::ranges for many reasons, including performance, the ability to create dangling references, and a cubic stack blowup when deeply nesting some range adapters like std::views::filter. John Bandela shared their custom range library design in Rappel: Compose algorithms, not iterators - Google's alternative to ranges. Instead of using iterators, which need to be pulled to receive new values one at a time, the library composes callbacks, which are eagerly invoked with the new values. This is faster because it doesn't involve a state machine, ensures that references can never outlive the user because the user code runs inside the algorithm, and only scales linearly in stack space because adapters don't need to repeatedly store pointers. Google's library isn't open source (yet?), but we at think-cell identified the problems with the iterator model decades ago and have built a very similar design in our own standard library, which you can already find on GitHub: It's really good to see that many different people converge on the same design, and I'm working on bringing some of it into the standard in the future.

Google does use coroutines, and Aaron Jacobs presented a smart way to prevent lifetime issues in Coroutines at scale - Implementation choices at Google. Consider the following coroutine:

Task<void> coroutine(std::string const& str) {
    co_await do_sth();
    std::print("{}\n", str);
A coroutine that takes a reference and uses it after a suspension point

A reference argument in a coroutine can easily dangle, as the referred object is not owned by the coroutine and needs to outlive the entire coroutine. So, it must not be called with a temporary object that's destroyed too early:

auto task = coroutine("hello");
co_await task;
Calling the coroutine with a dangling reference

The temporary std::string created in the first line is destroyed by the time we actually need it! However, Aaron realized that the following code is perfectly fine:

co_await coroutine("hello");
Calling the coroutine with a reference that lives long enough

Here, the temporary std::string is only destroyed after the co_await returns. So, to enforce the immediate use of co_await, the default task type in their library (called Co) is immovable, and co_await takes it by value. As such, you can only co_await prvalues, which allows the second use but not the first. If you do want to create the coroutine and await it later, you need to wrap it in a different type, which takes ownership over the temporary objects and keeps them alive until the coroutine finishes. It is pretty neat!

Functional C++, value-oriented programming, Carbon, and floats

Gašper Ažman presented his library for monadic error handling and composition in Functional C++. Using std::expected for error handling gets annoying when each operation returns a different type. He solved it by using expected<T, sum<E>>, where sum is a sum type (like std::variant). Combining an existing expected<T, sum<E>> in and_then with a function that returns an expected<U, sum<F>> results in an expected<T, sum<E, F>>, forcing you to handle both potential errors later on.

The library also features a pack<A, B, C> type, which is like std::tuple, but it is automatically unpacked when invoking a lambda. That way, you can have operations that return multiple values at once and forward them as individual arguments to the next function, instead of having to write everything in terms of std::tuple. Our think-cell-library does the same to make tc::zip(rng1, rng2) more straightforward to use.

Other talks I enjoyed watching were Tony Van Eerd's Value-oriented programming part V - Return of the values, where he describes how you can write better code by avoiding object-oriented programming and using value semantics instead; Generic Arity: Definition-checked variadics in Carbon by Geoffrey Romer, where he talks about the implementation of variadic templates in Carbon; and A new dragon in the den - Fast conversion from floating point numbers by Cassio Neri about his new, surprisingly simple float-to-string algorithm.

I also gave my talk, An (in-)complete guide to C++ object lifetimes, about everything from std::launder to std::start_lifetime_as. It was very well received, and I managed to have a prepared slide for almost all questions, which is always cool—of course, given that it's C++Now, there were always questions I could not answer.

Aspen itself was beautiful as always, and I ended the conference with a hike up the mountains. I always say that if you can only go to one conference, go to C++Now, so I hope to see you there next year!

— by Jonathan Müller

Do you have feedback? Send us a message at !

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.