C++26 Failed Its Ballot: Can You Still Use Contracts?
The C++26 draft failed its ballot, and your code already uses contracts with GCC 16. Is it safe to keep them in a library header that other teams compile?

- The C++26 draft failed both national-body ballot thresholds. Contracts have not automatically been removed.
- A contract can expose a bad call without preventing execution: the evaluation mode and violation handler determine what happens next.
- GCC 16 supports contracts experiments. Regular Clang releases do not yet support the same syntax, which matters for public headers.
A failed ballot changes the standardization process
C++26 has failed its Draft International Standard ballot. As of October 26, 2026, neither removing contracts nor a replacement publication schedule has been settled. For developers, the immediate consequence is uncertainty about the final specification. Compiler implementations of draft features do not disappear when a ballot fails.
The result published by Robert Seacord contains two different voting populations: 12 of 20 participating-member votes supported the draft, while 9 of 22 votes from all voting bodies opposed it. That is 60% support in the first calculation and roughly 41% opposition in the second. Subtracting 12 from 20 does not produce the second count because the denominators describe different groups.
ISO/IEC Directives §2.6.3 require at least two-thirds support among voting P-members and no more than one-quarter opposition overall. Abstentions are excluded. The draft missed both thresholds. These national-body votes also cannot be treated as the same electorate as earlier votes within the C++ working group.
A precondition is not an automatic error return
Consider a small illustrative function that divides a total among a positive number of people. The pre clause states what callers must provide. It does not define a fallback result or a user-facing error response.
int per_person(int total, int count)
pre(count > 0)
{
return total / count;
} Passing zero violates the precondition. Whether execution reaches the division depends on the evaluation semantics. The GCC contracts documentation describes modes that skip checking, report a violation and continue, or stop execution. These are materially different outcomes for the same source code.
| Semantics | Response to a violation | Effect on this example |
|---|---|---|
| ignore | No check runs. | The division by zero can be reached. |
| observe | The handler runs; execution continues if it returns normally. | A diagnostic can coexist with the invalid operation. |
| enforce | The program terminates if the handler returns normally. | Normal execution does not enter the function body. |
| quick-enforce | Termination occurs without calling the handler. | The violation stops execution immediately. |
Integer division by zero has undefined behavior. An observe-mode diagnostic can therefore be followed by invalid arithmetic. With enforce, normal return from the handler terminates the program. A handler defined to permit exceptions can instead throw, leaving the call through exception handling; crossing a noexcept boundary still causes termination. The contracts rationale, §3.6.6, explains this path.
If zero can arrive through an ordinary request, the application still needs input validation and an error path before calling this function. Contracts can document and check assumptions inside the program. They do not decide whether an invalid request should receive an error message, be retried, or use an alternative calculation. Those choices belong to the application.
The disagreement concerns the scope of the guarantee
P4334R0, by Bjarne Stroustrup and coauthors, supports the general idea of contracts while criticizing the current design. Its objections include manually supplied runtime checks, the ability to disable checking, complexity, and the absence of broader program guarantees. The authors want more than a mechanism for finding selected bad calls.
The example shows the limitation without requiring a large codebase. Checking that count is positive says nothing about an unrelated invalid pointer elsewhere. An omitted condition receives no protection, and a condition expression can itself contain an invalid operation. Making one boundary observable does not establish that every operation in the program is valid.
P4433R0, signed by Timur Doumler and 15 other authors, argues for retaining contracts. Its case treats configurable runtime checking as a complement to static guarantees, useful even when mandatory checking cannot be imposed everywhere. A team can add conditions to existing code and uncover violations during testing without claiming that the entire program has been proved safe.
These positions can describe the same behavior accurately and still disagree about adopting it. Observe mode may help discover a bug while failing to prevent the next invalid operation. The dispute is whether that benefit, the design complexity, and the available implementation experience justify the present standardization choice.
The papers also disagree about the experience already gathered. P4334R0 criticizes a library experiment limited to contract_assert, without new pre or post conditions or deployment at scale. P4433R0 responds that trials on multiple large codebases found existing bugs. Its authors argue that a feature with new declaration syntax should be judged against the experience available for other language features at the same stage of standardization.
Library hardening already depends on the distinction
For a hardened library precondition, merely observing failure can defeat the purpose of the check. Imagine a library detecting an out-of-range element access, invoking a handler, then performing the invalid access anyway. A useful diagnostic has occurred, but the checked boundary has not stopped execution from entering undefined behavior.
P3878R1 proposed terminating semantics for those hardened preconditions. P4433R0 reports that the change was adopted in November 2025, leaving enforce and quick-enforce rather than observe for this purpose. The requirements on a hardened standard-library check are consequently narrower than the range of modes available to contracts generally.
This dependency is part of the retention argument: removing contracts would require reworking library wording that relies on them. The P4433R0 authors argue that rewriting and reviewing those provisions would add work before another ballot, making removal more involved than simply extending the voting schedule.
Testing GCC support is different from shipping a header
The GCC C++26 status table lists P2900R14 contracts under GCC 16. The corresponding entry in the Clang support table is still “No.” The experimental Clang implementation discussed in the retention paper should not be mistaken for support in regular releases.
For a GCC 16 experiment, an explicit evaluation mode makes the intended behavior visible in the build configuration. Save the example function in contracts-example.cpp. This command compiles it to an object file, so the source does not need to contain main.
g++ -std=c++26 -fcontracts \
-fcontract-evaluation-semantic=enforce \
-c contracts-example.cpp GCC spells some option values differently from the standard terminology: ignored and observed select ignore and observe, while quick_enforce uses an underscore. Selecting a mode deliberately lets a team compare diagnostic collection with stopping behavior rather than leaving that expectation implicit.
A public header creates a different constraint. If it contains pre syntax, downstream compilers must parse that syntax too. Successful compilation in the library author's GCC build does not make the header usable by consumers on regular Clang releases. An internal trial can restrict its toolchain; a library that promises both toolchains cannot silently impose the same restriction.
Conditional compilation can hide unsupported syntax. GCC's status table lists the __cpp_contracts feature macro, but a syntax feature test does not establish which runtime evaluation mode was selected. Existing input handling and tests still need to work in configurations that omit contract syntax. Otherwise, introducing an optional check can accidentally become an undocumented change to the library's behavior.
Draft support also differs from a stable compatibility promise. GCC labels its C++26 support experimental. Pinning a compiler version for a trial makes it possible to compare later behavior; it does not establish that the trial's syntax and semantics will survive unchanged into the final standard.
The next draft will determine the next adoption decision
ISO/IEC Directives §2.6.4 provide several paths after an unsuccessful enquiry ballot, including an eight-week vote on a revised draft, recirculation at committee-draft level, or a draft Technical Specification. Cancellation also requires a committee decision. These are available procedures, not an announced C++26 timetable.
Mateusz Pusz's account of the result discusses the work of addressing the objections. The eight-week voting period is not a way to calculate a new publication month, and the ballot does not automatically rename the standard C++27.
An application targeting an existing standard can continue its current build. A C++26 experiment can record its compiler and checking mode, then evaluate the next draft against that baseline. A portable library can keep the trial outside its required public interface until its supported compilers and the specification provide a compatible route to adoption.