The C++ committee had its second hybrid meeting in Issaquah, WA in February, to finalize C++23. This was also the venue where we finished up C++14. I was there in person from Red Hat, while Thomas Rodgers and Jonathan Wakely attended virtually. As usual, I spent most of my time in the Core working group.
The primary goal of the meeting was to finish responding to national body comments on the draft and advance the result for balloting to become C++23, and indeed we did; the week went smoothly from my perspective. We also spent a fair amount of time reviewing papers and issues that were not expected to go into C++23.
Most of the C++23 fixes at this meeting were unremarkable, but a couple are worth mentioning:
CWG2518 clarifies that implementations need to reject input with #error or failing static_assert, but we didn't want the latter to apply to uninstantiated templates (where previously no diagnostic was required), so we folded in P2593 which makes a failing static_assert only ill-formed at instantiation time.
CWG2521 addressed uncertainty about how exactly user-defined literal suffixes should be treated like names; in particular, suffixes starting with an underscore should not be reserved to the implementation in the global namespace. To reduce the ambiguity, we decided to deprecate the form of UDL operator declaration with a space between the "" and the suffix.
Some of the NB comment resolutions from the November 2022 meeting are also worth calling out:
DE-046 (P2564): consteval needs to propagate up
If a function declared constexpr would be ill-formed because of trying to call a consteval function, promote it to consteval instead.
consteval int id(int i) { return i; }
template <typename T>
constexpr int f(T t) {
return t + id(t);
}
static_assert(f(3) == 6); // ok, f<int> promoted to consteval
DE-038 (P2718): temporary lifetime and range-based for
Dangling references to temporaries that are destroyed at the end of the initialization can be a problem in reference variable initialization, but are more of a hazard in range-based for loops because the reference being initialized is invisible to the user: in
for (auto e : T()[0]) { ... }
the T temporary is destroyed after the hidden range variable is initialized, so if the operator[] returns a reference, it's likely to be to an object that no longer exists by the time we get to the body of the loop. Normal temporary lifetime extension doesn't apply because the temporary is the operand of a call, not bound directly to the reference. This paper addresses the problem for range for by extending the lifetime of all temporaries in the range expression to cover the entire loop.
CA-065 (P2589): static operator[]
Since C++23 previously added static operator()
and multi-parameter operator[]
, it's consistent to also add static operator[]
.
GB-048 (P2647): Permitting static constexpr variables in constexpr functions
A piece we missed in all the other recent constexpr relaxations; there's no good reason to prohibit static local variables with constant initialization in constexpr functions.
US-16-045 (CWG2654): De-deprecating more volatile operations
We previously reverted the deprecation of bitwise compound assignment (&=
, |=
) to volatile lvalues due to feedback from the embedded community where such operations are common. This NB comment complained about treating bitwise operations differently from other arithmetic operations, so this resolution un-deprecates the rest of the compound assignment operatiors as well.
FR-019-055 (CWG2631) clarified that consteval
function calls in default arguments are constant-evaluated where the default argument is used, to be consistent with normal evaluation. This avoids the need for compilers to handle source_location::current
specially.
At the Issaquah meeting, Core also did initial reviews of various papers hoping to make C++26, including:
P2686 "constexpr structured bindings" proposes allowing structured bindings to be constexpr; this seems pretty straightforward to me.
P1061 "Structured Bindings can introduce a Pack" proposes allowing a structured binding to be a new kind of pack, like in variadic templates:
template <class F, class Tuple>
constexpr decltype(auto) apply(F &&f, Tuple &&t)
{
auto&& [...elems] = t; // elems is a structured binding pack
return std::invoke(std::forward<F>(f),
forward_like<Tuple, decltype(elems)>(elems)...);
}
An interesting thing about this proposal is that it can be used outside of templates, so the name of a structured binding pack would now be type-dependent even in a non-template context.
Various subgroups are working on less-stable proposals. The Tooling SG is working to find common ground between various compilers for how to work in a build system, particularly around C++20 modules. The Contracts SG is trying to agree on a minimum viable feature set to bring back the Contracts feature that was dropped from C++20. The Safety SG is looking at ways to balance performance and safety, continuing the reexamination of Undefined Behavior that has been going on for years.
The next meeting will be in Varna, Bulgaria in June, where we plan to do all C++26 work.