With P2900, we propose to add contract assertions to the C++ language. This proposal is in the final stages of wording review before being included in the draft Standard for C++26.

It has been suggested by some members of the C++ standard committee that this feature is too large, too complicated, and hard to teach. As it turns out, the opposite is true: contract assertions are actually very simple and can be explained in just five minutes. In this blog post, we will do exactly this!

As the name says, contract assertions are assertions — correctness checks that the programmer can add to their code to detect bugs at runtime. So they’re just like the existing assert macro, except they’re not macros (which fixes a bunch of problems) and they’re way more flexible and powerful!

contract_assert replaces assert

Our replacement for assert is called contract_assert. Unlike assert, contract_assert is a proper keyword, but it works in the same way:
 
auto w = getWidget(); 
contract_assert(w.isValid());  // a contract assertion
processWidget(w);
 
The default behaviour is also the same: the assertion is checked, and if the check fails, the program prints a diagnostic message and terminates.
 
With the assert macro, you can turn off the checks by defining another macro, NDEBUG, typically as a compiler flag. Similarly, you can turn off checks of contract assertions by selecting the ignore evaluation semantic. Clang and GCC offer the flag -fcontract-semantic=ignore for this purpose. This option is useful for example if you cannot afford to pay for the runtime checks in your production build, but still want to have them checked during development and testing.

Four build options: the evaluation semantics

When the assert macro is checked and the check fails, printing a message and terminating is the only option; this inflexibility is one reason why many people roll their own assertion macros.
 
Contract assertions as proposed in P2900 give you four evaluation semantics to choose from: 
  • ignore – do not check the assertion;
  • enforce — check the assertion; if the check fails, print a message and terminate (the default behaviour);
  • observe — check the assertion; if the check fails, print a message and continue;
  • quick-enforce — check the assertion; if the check fails, terminate immediately, without printing a message or doing anything else.
We already talked about ignore and enforce further above. The other two evaluation semantics are added to cover a wider variety of use cases (which in turn translates to more codebases being able to use contract assertions in more places):
 
Observe is useful if you are adding a new assertion to an existing codebase, you are not sure about the assertion itself, and you want to exclude the possibility of a false positive bringing down your production system. 
 
Quick-enforce minimises the runtime overhead of checks and is furthermore suited for applications where safety and security are paramount and “fail fast and reboot” is the optimal strategy to deal with a bug.   

Customising behaviour: the contract-violation handler

For even more flexibility, you can change the default “print a message” behaviour for observed and enforced contract assertions to any other bug-response behaviour suitable for your application by defining a global function called handle_contract_violation.
 
This user-defined contract-violation handler is effectively a callback where you can define your own preferred bug diagnosis strategy: custom logging, phoning home, printing a stacktrace, or even throwing an exception and unwinding the stack.
 
The contract-violation handler takes a single parameter of type const contract_violation&. This parameter will give you access to useful information about the violated assertion, such as its source location, evaluation semantic, whether an exception was thrown while the assertion was being checked, etc. 
 
Once you have defined a function handle_contract_violation with the correct signature, you install it by linking that definition into your program. It works exactly like installing a custom global operator new works today.

pre and post

Often, assertions express preconditions and postconditions of a function – conditions that are expected to hold true when the function is called and when it returns, respectively.
 
One major disadvantage of macros – shared with contract_assert even though the latter is not a macro – is that they go into the function body rather than on the function definition, making them invisible to the caller of that function.
 
This is where precondition assertions and postcondition assertions come in. They work exactly like contract_assert, except that they are spelled with pre and post and they can be placed on a function declaration: 
 
T& operator[] (size_t index) 
  pre (index < size());  // a precondition assertion 
 
void clear() 
  post (empty());  // a postcondition assertion 
 
pre is checked when the function is called (great for identifying buggy functions calls) while post is checked when the function returns (useful for identifying bugs inside the function itself). A function can have any number of pre and post specifiers.
 
With post, you can refer directly to the object returned by a function, a feature that is not available in C++ today:
 
Widget getWidget()
  post (ret: ret.isValid()); // you can use any other name instead of ret here!
 
Because pre and post are placed on the declaration of a function rather than inside its definition, they can be put into a shared header. There, they will be visible not only to the user of your code, but also to the compiler, the IDE, and to static analysis tools, vastly extending the ways in which contract assertions can be leveraged to diagnose and prevent bugs.

pr
e
and post can be applied to functions, templated functions, member functions, lambdas and coroutines; however, they cannot be applied to functions that are =defaulted, =deleted, or on virtual functions (virtual function support was present in an earlier version of the proposal, but then got removed at the C++ committee meeting in February 2025 in Hagenberg as the design for that is not yet fully baked).

And that’s it!

That’s all you need to know to get started with contract assertions! 
 
The formal proposal, P2900, has quite a few pages but the vast majority of all that standardese are technical details that the average user will either never have to worry about, or will discover on their own because they will see a compiler error when they get something wrong.
 
As is hopefully evident from the above, the learning curve for contract assertions as proposed for C++26 is an order of magnitude smoother than for features such as coroutines, concepts, or modules that we have standardised in the past.
 
If you want to know more about Contracts, feel free to check out Lisa Lippincott’s talk “Perspectives on Contracts” which explains the design principles underlying Contracts, and the Contracts and Safety for C++26 Roundtable, in which members of the Contracts Study Group (SG21) of the C++ standard committee explain how the proposal fits into the larger goal of making C++ safer and more secure.
 
While the feature is not yet part of the C++ Standard, it has already been fully implemented in Clang and GCC, and you can try these implementations today on Compiler Explorer (Clang linkGCC link). Have fun exploring, and let us know in case something isn’t working as expected!