C++20 reaches feature freeze without contracts
Cologne sends the C++20 draft to national-body review, removes contracts and adds library facilities for formatting and cooperative thread shutdown.
A language feature can enter a working draft and still leave before the revision is complete. Contracts did so at Cologne. Herb Sutter's July 20 meeting report says the committee could not resolve the remaining design disagreements in time for C++20. The principal proposers agreed to defer the feature while the committee sent the draft for its international comment ballot.
That decision accompanies feature freeze. The report describes a revision whose main capabilities are now selected, with subsequent work focused on national-body comments and corrections. It also records additions including text formatting and a joining thread with stop-token support.
I am more interested in what these decisions require a reader to understand than in the number of additions. C++ already contains several generations of conventions. A new feature should have a sufficiently clear contract that its short syntax does not conceal disagreement about its behavior.
Contracts required more than assertion syntax
The earlier P0542R5 contracts paper illustrates the scope of the design. It proposes function preconditions and postconditions, alongside assertions in function bodies. These annotations express requirements and guarantees at specified points.
The proposed notation also includes assertion levels. Its discussion covers whether name lookup depends on the selected build mode and how declarations repeat their contracts. Those are language integration questions. Attaching a condition to a function declaration requires rules for separate compilation as well as a rule for evaluating a Boolean expression.
For example, a function can be declared in more than one translation unit. If its declarations carry contracts, the specification has to define which declarations agree and whether a mismatch requires diagnosis. Logical equivalence between arbitrary expressions is not a simple textual comparison. The paper chooses a rule based on one-definition-rule equivalence with specified renaming allowances.
Violation handling introduces another dimension. A check can fail in code whose surrounding operation has exception and termination policies. The design has to explain how a violation handler interacts with those policies. The paper's discussion of a throwing handler shows why this is more demanding than adding a new spelling for an assert macro.
These details provide background for the size of the design task. They do not establish which particular disagreement caused each participant's vote at Cologne. Sutter's report gives the supported conclusion, that sufficient consensus on the feature's design had not been reached.
Removal defines the revision's actual scope
The committee moved further contracts work into a new study group, SG21, chaired by John Spicer. The report says the work will continue. That is a change of venue and schedule, with no guarantee in this report of a particular later outcome.
For a library author, the immediate consequence is clear. The C++20 draft being sent for review no longer supplies the contracts feature previously in it. An interface intended for that revision cannot assume the removed annotation mechanism as its standard foundation.
That matters to proposals written while contracts were still present. A paper can describe a precondition using terminology influenced by the working draft at the time. Reading the paper as evidence of the revision's final content requires checking what the committee actually retained.
This is why a feature-freeze account is useful even when the language design remains interesting. It tells implementers which capabilities belong to the document under review. The value is in the boundary of that document, not in predicting which postponed feature will return.
Joining threads connects lifetime to shutdown
The meeting adopted stop-token support and a joining thread. Sutter describes the latter as an RAII thread whose destructor joins if the thread remains joinable. The earlier P0660R9 proposal explains the design's cooperative stopping model.
A stop source can request stopping, while a stop token lets an operation observe the shared state. The request remains set once made. An operation can poll the state or arrange a callback when stopping is requested. The vocabulary is chosen to avoid implying that the requesting thread can forcibly interrupt arbitrary work.
That distinction is necessary for ordinary C++ resource management. Code needs an opportunity to leave scopes through paths where its invariants and destructors remain meaningful. A stop request communicates intent to the operation. The operation still has to respond.
I regard the joining behavior as a direct application of C++'s strongest ownership idea. A thread's lifetime should have an owner whose destruction does something specified. The policy is still observable, since joining may wait for work to finish. Automatic joining does not mean immediate destruction.
The proposal's example requests a stop before joining a joinable thread. If the worker observes that request and returns, its local resources can be released through its normal execution path. If it ignores the request, the mere existence of a token cannot guarantee prompt shutdown.
A token can reach a blocked operation
Polling alone is insufficient when work is asleep waiting for another event. P0660R9 includes callbacks associated with the stop state and condition-variable integration. These mechanisms provide a route for an operation to notice a request while it is waiting.
Callback registration has its own lifetime. The proposal discusses what happens when a request races with registration and when callback destruction must wait for an invocation already running on another thread. An object that registers a callback is therefore also responsible for the validity of the state the callback uses.
This illustrates the difference between a small calling interface and a simple implementation. A shared stopping mechanism can centralize that synchronization contract so each application does not invent another version. Users still need to understand when callbacks can run, especially if they capture local references.
The stop-token facility also stands apart from a particular thread object. The report describes it as a general mechanism that other types can use. That makes the division between requesting and observing useful beyond the lifetime of a single worker thread.
The stopping paper also specifies immediate callback invocation when registration occurs after a stop request. Registering is therefore an operation that can call application code before it returns. A callback that refers to an incompletely initialized owner would need to account for that possibility. This is a useful example of a lifetime requirement that remains even when the library supplies the synchronization around the shared stop state.
Formatting separates types from presentation
Cologne also adopted std::format. Victor Zverovich's P0645R9 paper, published in June, explains its combination of typed arguments and a format string. Replacement fields use braces, and formatting can produce a string without requiring an output stream.
A basic example of the adopted direction keeps the presentation text together. This illustrates the proposed standard interface, subject to toolchain implementation support:
#include <format>
#include <string>
std::string status(int count)
{
return std::format("processed {} entries", count);
}
The argument's type supplies information that printf format strings often encode through length modifiers. Presentation choices remain in the formatting language. This reduces the need for a caller to restate a C++ argument type as a separate format modifier.
The paper also provides customization for user-defined types through formatter specializations. A reusable type can define how formatting interprets its presentation options. That is an extensibility contract, and its parsing behavior belongs in the type's formatting support rather than in every call site.
The June proposal discusses format-string processing without requiring every string to be a compile-time constant. It is therefore important not to read type-safe arguments as a universal promise of compile-time detection for every malformed format string. The particular formatting interface and its error rules determine when a problem can be reported.
Review continues after feature selection
The report also records more constexpr library work, including vector and string, and broader application of the new comparison operator in the library. These are examples of the library adopting language mechanisms within the same revision. Their appearance in the draft still leaves implementation work for toolchains.
Feature freeze changes what that work is targeting. It gives the national bodies a selected feature set to examine and the committee a document against which to resolve comments. It does not declare that every wording issue has disappeared.
The contrast within this meeting is practical. Joining-thread ownership and formatting have specified mechanisms entering the draft, while contracts leave because the design still lacks sufficient agreement. For users planning around C++20, that difference determines which interface assumptions belong in the revision they are evaluating.